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_ncontextglobals', 172 'value': 'ScopeInfo::kContextGlobalCount' }, 173 { 'name': 'scopeinfo_idx_first_vars', 174 'value': 'ScopeInfo::kVariablePartIndex' }, 175 176 { 'name': 'sharedfunctioninfo_start_position_mask', 177 'value': 'SharedFunctionInfo::kStartPositionMask' }, 178 { 'name': 'sharedfunctioninfo_start_position_shift', 179 'value': 'SharedFunctionInfo::kStartPositionShift' }, 180 181 { 'name': 'jsarray_buffer_was_neutered_mask', 182 'value': 'JSArrayBuffer::WasNeutered::kMask' }, 183 { 'name': 'jsarray_buffer_was_neutered_shift', 184 'value': 'JSArrayBuffer::WasNeutered::kShift' }, 185 186 { 'name': 'context_idx_closure', 187 'value': 'Context::CLOSURE_INDEX' }, 188 { 'name': 'context_idx_native', 189 'value': 'Context::NATIVE_CONTEXT_INDEX' }, 190 { 'name': 'context_idx_prev', 191 'value': 'Context::PREVIOUS_INDEX' }, 192 { 'name': 'context_idx_ext', 193 'value': 'Context::EXTENSION_INDEX' }, 194 { 'name': 'context_min_slots', 195 'value': 'Context::MIN_CONTEXT_SLOTS' }, 196 197 { 'name': 'namedictionaryshape_prefix_size', 198 'value': 'NameDictionaryShape::kPrefixSize' }, 199 { 'name': 'namedictionaryshape_entry_size', 200 'value': 'NameDictionaryShape::kEntrySize' }, 201 202 { 'name': 'namedictionary_prefix_start_index', 203 'value': 'NameDictionary::kPrefixStartIndex' }, 204 205 { 'name': 'seedednumberdictionaryshape_prefix_size', 206 'value': 'SeededNumberDictionaryShape::kPrefixSize' }, 207 208 { 'name': 'unseedednumberdictionaryshape_prefix_size', 209 'value': 'UnseededNumberDictionaryShape::kPrefixSize' }, 210 211 { 'name': 'numberdictionaryshape_entry_size', 212 'value': 'NumberDictionaryShape::kEntrySize' } 213]; 214 215# 216# The following useful fields are missing accessors, so we define fake ones. 217# Please note that extra accessors should _only_ be added to expose offsets that 218# can be used to access actual V8 objects' properties. They should not be added 219# for exposing other values. For instance, enumeration values or class' 220# constants should be exposed by adding an entry in the "consts_misc" table, not 221# in this "extras_accessors" table. 222# 223extras_accessors = [ 224 'JSFunction, context, Context, kContextOffset', 225 'HeapObject, map, Map, kMapOffset', 226 'JSObject, elements, Object, kElementsOffset', 227 'FixedArray, data, uintptr_t, kHeaderSize', 228 'JSArrayBuffer, backing_store, Object, kBackingStoreOffset', 229 'JSArrayBufferView, byte_offset, Object, kByteOffsetOffset', 230 'JSTypedArray, length, Object, kLengthOffset', 231 'Map, instance_attributes, int, kInstanceAttributesOffset', 232 'Map, inobject_properties_or_constructor_function_index, int, kInObjectPropertiesOrConstructorFunctionIndexOffset', 233 'Map, instance_size, int, kInstanceSizeOffset', 234 'Map, bit_field, char, kBitFieldOffset', 235 'Map, bit_field2, char, kBitField2Offset', 236 'Map, bit_field3, int, kBitField3Offset', 237 'Map, prototype, Object, kPrototypeOffset', 238 'Oddball, kind_offset, int, kKindOffset', 239 'HeapNumber, value, double, kValueOffset', 240 'ConsString, first, String, kFirstOffset', 241 'ConsString, second, String, kSecondOffset', 242 'ExternalString, resource, Object, kResourceOffset', 243 'SeqOneByteString, chars, char, kHeaderSize', 244 'SeqTwoByteString, chars, char, kHeaderSize', 245 'SharedFunctionInfo, code, Code, kCodeOffset', 246 'SharedFunctionInfo, scope_info, ScopeInfo, kScopeInfoOffset', 247 'SlicedString, parent, String, kParentOffset', 248 'Code, instruction_start, uintptr_t, kHeaderSize', 249 'Code, instruction_size, int, kInstructionSizeOffset', 250]; 251 252# 253# The following is a whitelist of classes we expect to find when scanning the 254# source code. This list is not exhaustive, but it's still useful to identify 255# when this script gets out of sync with the source. See load_objects(). 256# 257expected_classes = [ 258 'ConsString', 'FixedArray', 'HeapNumber', 'JSArray', 'JSFunction', 259 'JSObject', 'JSRegExp', 'JSValue', 'Map', 'Oddball', 'Script', 260 'SeqOneByteString', 'SharedFunctionInfo' 261]; 262 263 264# 265# The following structures store high-level representations of the structures 266# for which we're going to emit descriptive constants. 267# 268types = {}; # set of all type names 269typeclasses = {}; # maps type names to corresponding class names 270klasses = {}; # known classes, including parents 271fields = []; # field declarations 272 273header = ''' 274/* 275 * This file is generated by %s. Do not edit directly. 276 */ 277 278#include "src/v8.h" 279#include "src/frames.h" 280#include "src/frames-inl.h" /* for architecture-specific frame constants */ 281#include "src/contexts.h" 282 283using namespace v8::internal; 284 285extern "C" { 286 287/* stack frame constants */ 288#define FRAME_CONST(value, klass) \ 289 int v8dbg_frametype_##klass = StackFrame::value; 290 291STACK_FRAME_TYPE_LIST(FRAME_CONST) 292 293#undef FRAME_CONST 294 295''' % sys.argv[0]; 296 297footer = ''' 298} 299''' 300 301# 302# Get the base class 303# 304def get_base_class(klass): 305 if (klass == 'Object'): 306 return klass; 307 308 if (not (klass in klasses)): 309 return None; 310 311 k = klasses[klass]; 312 313 return get_base_class(k['parent']); 314 315# 316# Loads class hierarchy and type information from "objects.h". 317# 318def load_objects(): 319 objfilename = sys.argv[2]; 320 objfile = open(objfilename, 'r'); 321 in_insttype = False; 322 323 typestr = ''; 324 325 # 326 # Construct a dictionary for the classes we're sure should be present. 327 # 328 checktypes = {}; 329 for klass in expected_classes: 330 checktypes[klass] = True; 331 332 # 333 # Iterate objects.h line-by-line to collect type and class information. 334 # For types, we accumulate a string representing the entire InstanceType 335 # enum definition and parse it later because it's easier to do so 336 # without the embedded newlines. 337 # 338 for line in objfile: 339 if (line.startswith('enum InstanceType {')): 340 in_insttype = True; 341 continue; 342 343 if (in_insttype and line.startswith('};')): 344 in_insttype = False; 345 continue; 346 347 line = re.sub('//.*', '', line.strip()); 348 349 if (in_insttype): 350 typestr += line; 351 continue; 352 353 match = re.match('class (\w[^:]*)(: public (\w[^{]*))?\s*{\s*', 354 line); 355 356 if (match): 357 klass = match.group(1).strip(); 358 pklass = match.group(3); 359 if (pklass): 360 pklass = pklass.strip(); 361 klasses[klass] = { 'parent': pklass }; 362 363 # 364 # Process the instance type declaration. 365 # 366 entries = typestr.split(','); 367 for entry in entries: 368 types[re.sub('\s*=.*', '', entry).lstrip()] = True; 369 370 # 371 # Infer class names for each type based on a systematic transformation. 372 # For example, "JS_FUNCTION_TYPE" becomes "JSFunction". We find the 373 # class for each type rather than the other way around because there are 374 # fewer cases where one type maps to more than one class than the other 375 # way around. 376 # 377 for type in types: 378 # 379 # Symbols and Strings are implemented using the same classes. 380 # 381 usetype = re.sub('SYMBOL_', 'STRING_', type); 382 383 # 384 # REGEXP behaves like REG_EXP, as in JS_REGEXP_TYPE => JSRegExp. 385 # 386 usetype = re.sub('_REGEXP_', '_REG_EXP_', usetype); 387 388 # 389 # Remove the "_TYPE" suffix and then convert to camel case, 390 # except that a "JS" prefix remains uppercase (as in 391 # "JS_FUNCTION_TYPE" => "JSFunction"). 392 # 393 if (not usetype.endswith('_TYPE')): 394 continue; 395 396 usetype = usetype[0:len(usetype) - len('_TYPE')]; 397 parts = usetype.split('_'); 398 cctype = ''; 399 400 if (parts[0] == 'JS'): 401 cctype = 'JS'; 402 start = 1; 403 else: 404 cctype = ''; 405 start = 0; 406 407 for ii in range(start, len(parts)): 408 part = parts[ii]; 409 cctype += part[0].upper() + part[1:].lower(); 410 411 # 412 # Mapping string types is more complicated. Both types and 413 # class names for Strings specify a representation (e.g., Seq, 414 # Cons, External, or Sliced) and an encoding (TwoByte/OneByte), 415 # In the simplest case, both of these are explicit in both 416 # names, as in: 417 # 418 # EXTERNAL_ONE_BYTE_STRING_TYPE => ExternalOneByteString 419 # 420 # However, either the representation or encoding can be omitted 421 # from the type name, in which case "Seq" and "TwoByte" are 422 # assumed, as in: 423 # 424 # STRING_TYPE => SeqTwoByteString 425 # 426 # Additionally, sometimes the type name has more information 427 # than the class, as in: 428 # 429 # CONS_ONE_BYTE_STRING_TYPE => ConsString 430 # 431 # To figure this out dynamically, we first check for a 432 # representation and encoding and add them if they're not 433 # present. If that doesn't yield a valid class name, then we 434 # strip out the representation. 435 # 436 if (cctype.endswith('String')): 437 if (cctype.find('Cons') == -1 and 438 cctype.find('External') == -1 and 439 cctype.find('Sliced') == -1): 440 if (cctype.find('OneByte') != -1): 441 cctype = re.sub('OneByteString$', 442 'SeqOneByteString', cctype); 443 else: 444 cctype = re.sub('String$', 445 'SeqString', cctype); 446 447 if (cctype.find('OneByte') == -1): 448 cctype = re.sub('String$', 'TwoByteString', 449 cctype); 450 451 if (not (cctype in klasses)): 452 cctype = re.sub('OneByte', '', cctype); 453 cctype = re.sub('TwoByte', '', cctype); 454 455 # 456 # Despite all that, some types have no corresponding class. 457 # 458 if (cctype in klasses): 459 typeclasses[type] = cctype; 460 if (cctype in checktypes): 461 del checktypes[cctype]; 462 463 if (len(checktypes) > 0): 464 for klass in checktypes: 465 print('error: expected class \"%s\" not found' % klass); 466 467 sys.exit(1); 468 469 470# 471# For a given macro call, pick apart the arguments and return an object 472# describing the corresponding output constant. See load_fields(). 473# 474def parse_field(call): 475 # Replace newlines with spaces. 476 for ii in range(0, len(call)): 477 if (call[ii] == '\n'): 478 call[ii] == ' '; 479 480 idx = call.find('('); 481 kind = call[0:idx]; 482 rest = call[idx + 1: len(call) - 1]; 483 args = re.split('\s*,\s*', rest); 484 485 consts = []; 486 487 if (kind == 'ACCESSORS' or kind == 'ACCESSORS_GCSAFE'): 488 klass = args[0]; 489 field = args[1]; 490 dtype = args[2]; 491 offset = args[3]; 492 493 return ({ 494 'name': 'class_%s__%s__%s' % (klass, field, dtype), 495 'value': '%s::%s' % (klass, offset) 496 }); 497 498 assert(kind == 'SMI_ACCESSORS' or kind == 'ACCESSORS_TO_SMI'); 499 klass = args[0]; 500 field = args[1]; 501 offset = args[2]; 502 503 return ({ 504 'name': 'class_%s__%s__%s' % (klass, field, 'SMI'), 505 'value': '%s::%s' % (klass, offset) 506 }); 507 508# 509# Load field offset information from objects-inl.h. 510# 511def load_fields(): 512 inlfilename = sys.argv[3]; 513 inlfile = open(inlfilename, 'r'); 514 515 # 516 # Each class's fields and the corresponding offsets are described in the 517 # source by calls to macros like "ACCESSORS" (and friends). All we do 518 # here is extract these macro invocations, taking into account that they 519 # may span multiple lines and may contain nested parentheses. We also 520 # call parse_field() to pick apart the invocation. 521 # 522 prefixes = [ 'ACCESSORS', 'ACCESSORS_GCSAFE', 523 'SMI_ACCESSORS', 'ACCESSORS_TO_SMI' ]; 524 current = ''; 525 opens = 0; 526 527 for line in inlfile: 528 if (opens > 0): 529 # Continuation line 530 for ii in range(0, len(line)): 531 if (line[ii] == '('): 532 opens += 1; 533 elif (line[ii] == ')'): 534 opens -= 1; 535 536 if (opens == 0): 537 break; 538 539 current += line[0:ii + 1]; 540 continue; 541 542 for prefix in prefixes: 543 if (not line.startswith(prefix + '(')): 544 continue; 545 546 if (len(current) > 0): 547 fields.append(parse_field(current)); 548 current = ''; 549 550 for ii in range(len(prefix), len(line)): 551 if (line[ii] == '('): 552 opens += 1; 553 elif (line[ii] == ')'): 554 opens -= 1; 555 556 if (opens == 0): 557 break; 558 559 current += line[0:ii + 1]; 560 561 if (len(current) > 0): 562 fields.append(parse_field(current)); 563 current = ''; 564 565 for body in extras_accessors: 566 fields.append(parse_field('ACCESSORS(%s)' % body)); 567 568# 569# Emit a block of constants. 570# 571def emit_set(out, consts): 572 # Fix up overzealous parses. This could be done inside the 573 # parsers but as there are several, it's easiest to do it here. 574 ws = re.compile('\s+') 575 for const in consts: 576 name = ws.sub('', const['name']) 577 value = ws.sub('', str(const['value'])) # Can be a number. 578 out.write('int v8dbg_%s = %s;\n' % (name, value)) 579 out.write('\n'); 580 581# 582# Emit the whole output file. 583# 584def emit_config(): 585 out = file(sys.argv[1], 'w'); 586 587 out.write(header); 588 589 out.write('/* miscellaneous constants */\n'); 590 emit_set(out, consts_misc); 591 592 out.write('/* class type information */\n'); 593 consts = []; 594 keys = typeclasses.keys(); 595 keys.sort(); 596 for typename in keys: 597 klass = typeclasses[typename]; 598 consts.append({ 599 'name': 'type_%s__%s' % (klass, typename), 600 'value': typename 601 }); 602 603 emit_set(out, consts); 604 605 out.write('/* class hierarchy information */\n'); 606 consts = []; 607 keys = klasses.keys(); 608 keys.sort(); 609 for klassname in keys: 610 pklass = klasses[klassname]['parent']; 611 bklass = get_base_class(klassname); 612 if (bklass != 'Object'): 613 continue; 614 if (pklass == None): 615 continue; 616 617 consts.append({ 618 'name': 'parent_%s__%s' % (klassname, pklass), 619 'value': 0 620 }); 621 622 emit_set(out, consts); 623 624 out.write('/* field information */\n'); 625 emit_set(out, fields); 626 627 out.write(footer); 628 629if (len(sys.argv) < 4): 630 print('usage: %s output.cc objects.h objects-inl.h' % sys.argv[0]); 631 sys.exit(2); 632 633load_objects(); 634load_fields(); 635emit_config(); 636