1 //===-- Value.cpp -----------------------------------------------*- C++ -*-===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "lldb/Core/Value.h" 11 12 // C Includes 13 // C++ Includes 14 // Other libraries and framework includes 15 // Project includes 16 #include "lldb/Core/DataBufferHeap.h" 17 #include "lldb/Core/DataExtractor.h" 18 #include "lldb/Core/Module.h" 19 #include "lldb/Core/State.h" 20 #include "lldb/Core/Stream.h" 21 #include "lldb/Symbol/ClangASTContext.h" 22 #include "lldb/Symbol/CompilerType.h" 23 #include "lldb/Symbol/ObjectFile.h" 24 #include "lldb/Symbol/SymbolContext.h" 25 #include "lldb/Symbol/Type.h" 26 #include "lldb/Symbol/Variable.h" 27 #include "lldb/Target/ExecutionContext.h" 28 #include "lldb/Target/Process.h" 29 #include "lldb/Target/SectionLoadList.h" 30 #include "lldb/Target/Target.h" 31 32 using namespace lldb; 33 using namespace lldb_private; 34 35 Value::Value() 36 : m_value(), m_vector(), m_compiler_type(), m_context(NULL), 37 m_value_type(eValueTypeScalar), m_context_type(eContextTypeInvalid), 38 m_data_buffer() {} 39 40 Value::Value(const Scalar &scalar) 41 : m_value(scalar), m_vector(), m_compiler_type(), m_context(NULL), 42 m_value_type(eValueTypeScalar), m_context_type(eContextTypeInvalid), 43 m_data_buffer() {} 44 45 Value::Value(const void *bytes, int len) 46 : m_value(), m_vector(), m_compiler_type(), m_context(NULL), 47 m_value_type(eValueTypeHostAddress), m_context_type(eContextTypeInvalid), 48 m_data_buffer() { 49 SetBytes(bytes, len); 50 } 51 52 Value::Value(const Value &v) 53 : m_value(v.m_value), m_vector(v.m_vector), 54 m_compiler_type(v.m_compiler_type), m_context(v.m_context), 55 m_value_type(v.m_value_type), m_context_type(v.m_context_type), 56 m_data_buffer() { 57 const uintptr_t rhs_value = 58 (uintptr_t)v.m_value.ULongLong(LLDB_INVALID_ADDRESS); 59 if ((rhs_value != 0) && 60 (rhs_value == (uintptr_t)v.m_data_buffer.GetBytes())) { 61 m_data_buffer.CopyData(v.m_data_buffer.GetBytes(), 62 v.m_data_buffer.GetByteSize()); 63 64 m_value = (uintptr_t)m_data_buffer.GetBytes(); 65 } 66 } 67 68 Value &Value::operator=(const Value &rhs) { 69 if (this != &rhs) { 70 m_value = rhs.m_value; 71 m_vector = rhs.m_vector; 72 m_compiler_type = rhs.m_compiler_type; 73 m_context = rhs.m_context; 74 m_value_type = rhs.m_value_type; 75 m_context_type = rhs.m_context_type; 76 const uintptr_t rhs_value = 77 (uintptr_t)rhs.m_value.ULongLong(LLDB_INVALID_ADDRESS); 78 if ((rhs_value != 0) && 79 (rhs_value == (uintptr_t)rhs.m_data_buffer.GetBytes())) { 80 m_data_buffer.CopyData(rhs.m_data_buffer.GetBytes(), 81 rhs.m_data_buffer.GetByteSize()); 82 83 m_value = (uintptr_t)m_data_buffer.GetBytes(); 84 } 85 } 86 return *this; 87 } 88 89 void Value::SetBytes(const void *bytes, int len) { 90 m_value_type = eValueTypeHostAddress; 91 m_data_buffer.CopyData(bytes, len); 92 m_value = (uintptr_t)m_data_buffer.GetBytes(); 93 } 94 95 void Value::AppendBytes(const void *bytes, int len) { 96 m_value_type = eValueTypeHostAddress; 97 m_data_buffer.AppendData(bytes, len); 98 m_value = (uintptr_t)m_data_buffer.GetBytes(); 99 } 100 101 void Value::Dump(Stream *strm) { 102 m_value.GetValue(strm, true); 103 strm->Printf(", value_type = %s, context = %p, context_type = %s", 104 Value::GetValueTypeAsCString(m_value_type), m_context, 105 Value::GetContextTypeAsCString(m_context_type)); 106 } 107 108 Value::ValueType Value::GetValueType() const { return m_value_type; } 109 110 AddressType Value::GetValueAddressType() const { 111 switch (m_value_type) { 112 default: 113 case eValueTypeScalar: 114 break; 115 case eValueTypeLoadAddress: 116 return eAddressTypeLoad; 117 case eValueTypeFileAddress: 118 return eAddressTypeFile; 119 case eValueTypeHostAddress: 120 return eAddressTypeHost; 121 } 122 return eAddressTypeInvalid; 123 } 124 125 RegisterInfo *Value::GetRegisterInfo() const { 126 if (m_context_type == eContextTypeRegisterInfo) 127 return static_cast<RegisterInfo *>(m_context); 128 return NULL; 129 } 130 131 Type *Value::GetType() { 132 if (m_context_type == eContextTypeLLDBType) 133 return static_cast<Type *>(m_context); 134 return NULL; 135 } 136 137 size_t Value::AppendDataToHostBuffer(const Value &rhs) { 138 size_t curr_size = m_data_buffer.GetByteSize(); 139 Error error; 140 switch (rhs.GetValueType()) { 141 case eValueTypeScalar: { 142 const size_t scalar_size = rhs.m_value.GetByteSize(); 143 if (scalar_size > 0) { 144 const size_t new_size = curr_size + scalar_size; 145 if (ResizeData(new_size) == new_size) { 146 rhs.m_value.GetAsMemoryData(m_data_buffer.GetBytes() + curr_size, 147 scalar_size, endian::InlHostByteOrder(), 148 error); 149 return scalar_size; 150 } 151 } 152 } break; 153 case eValueTypeVector: { 154 const size_t vector_size = rhs.m_vector.length; 155 if (vector_size > 0) { 156 const size_t new_size = curr_size + vector_size; 157 if (ResizeData(new_size) == new_size) { 158 ::memcpy(m_data_buffer.GetBytes() + curr_size, rhs.m_vector.bytes, 159 vector_size); 160 return vector_size; 161 } 162 } 163 } break; 164 case eValueTypeFileAddress: 165 case eValueTypeLoadAddress: 166 case eValueTypeHostAddress: { 167 const uint8_t *src = rhs.GetBuffer().GetBytes(); 168 const size_t src_len = rhs.GetBuffer().GetByteSize(); 169 if (src && src_len > 0) { 170 const size_t new_size = curr_size + src_len; 171 if (ResizeData(new_size) == new_size) { 172 ::memcpy(m_data_buffer.GetBytes() + curr_size, src, src_len); 173 return src_len; 174 } 175 } 176 } break; 177 } 178 return 0; 179 } 180 181 size_t Value::ResizeData(size_t len) { 182 m_value_type = eValueTypeHostAddress; 183 m_data_buffer.SetByteSize(len); 184 m_value = (uintptr_t)m_data_buffer.GetBytes(); 185 return m_data_buffer.GetByteSize(); 186 } 187 188 bool Value::ValueOf(ExecutionContext *exe_ctx) { 189 switch (m_context_type) { 190 case eContextTypeInvalid: 191 case eContextTypeRegisterInfo: // RegisterInfo * 192 case eContextTypeLLDBType: // Type * 193 break; 194 195 case eContextTypeVariable: // Variable * 196 ResolveValue(exe_ctx); 197 return true; 198 } 199 return false; 200 } 201 202 uint64_t Value::GetValueByteSize(Error *error_ptr, ExecutionContext *exe_ctx) { 203 uint64_t byte_size = 0; 204 205 switch (m_context_type) { 206 case eContextTypeRegisterInfo: // RegisterInfo * 207 if (GetRegisterInfo()) 208 byte_size = GetRegisterInfo()->byte_size; 209 break; 210 211 case eContextTypeInvalid: 212 case eContextTypeLLDBType: // Type * 213 case eContextTypeVariable: // Variable * 214 { 215 const CompilerType &ast_type = GetCompilerType(); 216 if (ast_type.IsValid()) 217 byte_size = ast_type.GetByteSize( 218 exe_ctx ? exe_ctx->GetBestExecutionContextScope() : nullptr); 219 } break; 220 } 221 222 if (error_ptr) { 223 if (byte_size == 0) { 224 if (error_ptr->Success()) 225 error_ptr->SetErrorString("Unable to determine byte size."); 226 } else { 227 error_ptr->Clear(); 228 } 229 } 230 return byte_size; 231 } 232 233 const CompilerType &Value::GetCompilerType() { 234 if (!m_compiler_type.IsValid()) { 235 switch (m_context_type) { 236 case eContextTypeInvalid: 237 break; 238 239 case eContextTypeRegisterInfo: 240 break; // TODO: Eventually convert into a compiler type? 241 242 case eContextTypeLLDBType: { 243 Type *lldb_type = GetType(); 244 if (lldb_type) 245 m_compiler_type = lldb_type->GetForwardCompilerType(); 246 } break; 247 248 case eContextTypeVariable: { 249 Variable *variable = GetVariable(); 250 if (variable) { 251 Type *variable_type = variable->GetType(); 252 if (variable_type) 253 m_compiler_type = variable_type->GetForwardCompilerType(); 254 } 255 } break; 256 } 257 } 258 259 return m_compiler_type; 260 } 261 262 void Value::SetCompilerType(const CompilerType &compiler_type) { 263 m_compiler_type = compiler_type; 264 } 265 266 lldb::Format Value::GetValueDefaultFormat() { 267 switch (m_context_type) { 268 case eContextTypeRegisterInfo: 269 if (GetRegisterInfo()) 270 return GetRegisterInfo()->format; 271 break; 272 273 case eContextTypeInvalid: 274 case eContextTypeLLDBType: 275 case eContextTypeVariable: { 276 const CompilerType &ast_type = GetCompilerType(); 277 if (ast_type.IsValid()) 278 return ast_type.GetFormat(); 279 } break; 280 } 281 282 // Return a good default in case we can't figure anything out 283 return eFormatHex; 284 } 285 286 bool Value::GetData(DataExtractor &data) { 287 switch (m_value_type) { 288 default: 289 break; 290 291 case eValueTypeScalar: 292 if (m_value.GetData(data)) 293 return true; 294 break; 295 296 case eValueTypeLoadAddress: 297 case eValueTypeFileAddress: 298 case eValueTypeHostAddress: 299 if (m_data_buffer.GetByteSize()) { 300 data.SetData(m_data_buffer.GetBytes(), m_data_buffer.GetByteSize(), 301 data.GetByteOrder()); 302 return true; 303 } 304 break; 305 } 306 307 return false; 308 } 309 310 Error Value::GetValueAsData(ExecutionContext *exe_ctx, DataExtractor &data, 311 uint32_t data_offset, Module *module) { 312 data.Clear(); 313 314 Error error; 315 lldb::addr_t address = LLDB_INVALID_ADDRESS; 316 AddressType address_type = eAddressTypeFile; 317 Address file_so_addr; 318 const CompilerType &ast_type = GetCompilerType(); 319 switch (m_value_type) { 320 case eValueTypeVector: 321 if (ast_type.IsValid()) 322 data.SetAddressByteSize(ast_type.GetPointerByteSize()); 323 else 324 data.SetAddressByteSize(sizeof(void *)); 325 data.SetData(m_vector.bytes, m_vector.length, m_vector.byte_order); 326 break; 327 328 case eValueTypeScalar: { 329 data.SetByteOrder(endian::InlHostByteOrder()); 330 if (ast_type.IsValid()) 331 data.SetAddressByteSize(ast_type.GetPointerByteSize()); 332 else 333 data.SetAddressByteSize(sizeof(void *)); 334 335 uint32_t limit_byte_size = UINT32_MAX; 336 337 if (ast_type.IsValid()) { 338 limit_byte_size = ast_type.GetByteSize( 339 exe_ctx ? exe_ctx->GetBestExecutionContextScope() : nullptr); 340 } 341 342 if (limit_byte_size <= m_value.GetByteSize()) { 343 if (m_value.GetData(data, limit_byte_size)) 344 return error; // Success; 345 } 346 347 error.SetErrorStringWithFormat("extracting data from value failed"); 348 break; 349 } 350 case eValueTypeLoadAddress: 351 if (exe_ctx == NULL) { 352 error.SetErrorString("can't read load address (no execution context)"); 353 } else { 354 Process *process = exe_ctx->GetProcessPtr(); 355 if (process == NULL || !process->IsAlive()) { 356 Target *target = exe_ctx->GetTargetPtr(); 357 if (target) { 358 // Allow expressions to run and evaluate things when the target 359 // has memory sections loaded. This allows you to use "target modules 360 // load" 361 // to load your executable and any shared libraries, then execute 362 // commands where you can look at types in data sections. 363 const SectionLoadList &target_sections = target->GetSectionLoadList(); 364 if (!target_sections.IsEmpty()) { 365 address = m_value.ULongLong(LLDB_INVALID_ADDRESS); 366 if (target_sections.ResolveLoadAddress(address, file_so_addr)) { 367 address_type = eAddressTypeLoad; 368 data.SetByteOrder(target->GetArchitecture().GetByteOrder()); 369 data.SetAddressByteSize( 370 target->GetArchitecture().GetAddressByteSize()); 371 } else 372 address = LLDB_INVALID_ADDRESS; 373 } 374 // else 375 // { 376 // ModuleSP exe_module_sp 377 // (target->GetExecutableModule()); 378 // if (exe_module_sp) 379 // { 380 // address = 381 // m_value.ULongLong(LLDB_INVALID_ADDRESS); 382 // if (address != LLDB_INVALID_ADDRESS) 383 // { 384 // if 385 // (exe_module_sp->ResolveFileAddress(address, 386 // file_so_addr)) 387 // { 388 // data.SetByteOrder(target->GetArchitecture().GetByteOrder()); 389 // data.SetAddressByteSize(target->GetArchitecture().GetAddressByteSize()); 390 // address_type = eAddressTypeFile; 391 // } 392 // else 393 // { 394 // address = LLDB_INVALID_ADDRESS; 395 // } 396 // } 397 // } 398 // } 399 } else { 400 error.SetErrorString("can't read load address (invalid process)"); 401 } 402 } else { 403 address = m_value.ULongLong(LLDB_INVALID_ADDRESS); 404 address_type = eAddressTypeLoad; 405 data.SetByteOrder( 406 process->GetTarget().GetArchitecture().GetByteOrder()); 407 data.SetAddressByteSize( 408 process->GetTarget().GetArchitecture().GetAddressByteSize()); 409 } 410 } 411 break; 412 413 case eValueTypeFileAddress: 414 if (exe_ctx == NULL) { 415 error.SetErrorString("can't read file address (no execution context)"); 416 } else if (exe_ctx->GetTargetPtr() == NULL) { 417 error.SetErrorString("can't read file address (invalid target)"); 418 } else { 419 address = m_value.ULongLong(LLDB_INVALID_ADDRESS); 420 if (address == LLDB_INVALID_ADDRESS) { 421 error.SetErrorString("invalid file address"); 422 } else { 423 if (module == NULL) { 424 // The only thing we can currently lock down to a module so that 425 // we can resolve a file address, is a variable. 426 Variable *variable = GetVariable(); 427 if (variable) { 428 SymbolContext var_sc; 429 variable->CalculateSymbolContext(&var_sc); 430 module = var_sc.module_sp.get(); 431 } 432 } 433 434 if (module) { 435 bool resolved = false; 436 ObjectFile *objfile = module->GetObjectFile(); 437 if (objfile) { 438 Address so_addr(address, objfile->GetSectionList()); 439 addr_t load_address = 440 so_addr.GetLoadAddress(exe_ctx->GetTargetPtr()); 441 bool process_launched_and_stopped = 442 exe_ctx->GetProcessPtr() 443 ? StateIsStoppedState(exe_ctx->GetProcessPtr()->GetState(), 444 true /* must_exist */) 445 : false; 446 // Don't use the load address if the process has exited. 447 if (load_address != LLDB_INVALID_ADDRESS && 448 process_launched_and_stopped) { 449 resolved = true; 450 address = load_address; 451 address_type = eAddressTypeLoad; 452 data.SetByteOrder( 453 exe_ctx->GetTargetRef().GetArchitecture().GetByteOrder()); 454 data.SetAddressByteSize(exe_ctx->GetTargetRef() 455 .GetArchitecture() 456 .GetAddressByteSize()); 457 } else { 458 if (so_addr.IsSectionOffset()) { 459 resolved = true; 460 file_so_addr = so_addr; 461 data.SetByteOrder(objfile->GetByteOrder()); 462 data.SetAddressByteSize(objfile->GetAddressByteSize()); 463 } 464 } 465 } 466 if (!resolved) { 467 Variable *variable = GetVariable(); 468 469 if (module) { 470 if (variable) 471 error.SetErrorStringWithFormat( 472 "unable to resolve the module for file address 0x%" PRIx64 473 " for variable '%s' in %s", 474 address, variable->GetName().AsCString(""), 475 module->GetFileSpec().GetPath().c_str()); 476 else 477 error.SetErrorStringWithFormat( 478 "unable to resolve the module for file address 0x%" PRIx64 479 " in %s", 480 address, module->GetFileSpec().GetPath().c_str()); 481 } else { 482 if (variable) 483 error.SetErrorStringWithFormat( 484 "unable to resolve the module for file address 0x%" PRIx64 485 " for variable '%s'", 486 address, variable->GetName().AsCString("")); 487 else 488 error.SetErrorStringWithFormat( 489 "unable to resolve the module for file address 0x%" PRIx64, 490 address); 491 } 492 } 493 } else { 494 // Can't convert a file address to anything valid without more 495 // context (which Module it came from) 496 error.SetErrorString( 497 "can't read memory from file address without more context"); 498 } 499 } 500 } 501 break; 502 503 case eValueTypeHostAddress: 504 address = m_value.ULongLong(LLDB_INVALID_ADDRESS); 505 address_type = eAddressTypeHost; 506 if (exe_ctx) { 507 Target *target = exe_ctx->GetTargetPtr(); 508 if (target) { 509 data.SetByteOrder(target->GetArchitecture().GetByteOrder()); 510 data.SetAddressByteSize(target->GetArchitecture().GetAddressByteSize()); 511 break; 512 } 513 } 514 // fallback to host settings 515 data.SetByteOrder(endian::InlHostByteOrder()); 516 data.SetAddressByteSize(sizeof(void *)); 517 break; 518 } 519 520 // Bail if we encountered any errors 521 if (error.Fail()) 522 return error; 523 524 if (address == LLDB_INVALID_ADDRESS) { 525 error.SetErrorStringWithFormat("invalid %s address", 526 address_type == eAddressTypeHost ? "host" 527 : "load"); 528 return error; 529 } 530 531 // If we got here, we need to read the value from memory 532 size_t byte_size = GetValueByteSize(&error, exe_ctx); 533 534 // Bail if we encountered any errors getting the byte size 535 if (error.Fail()) 536 return error; 537 538 // Make sure we have enough room within "data", and if we don't make 539 // something large enough that does 540 if (!data.ValidOffsetForDataOfSize(data_offset, byte_size)) { 541 DataBufferSP data_sp(new DataBufferHeap(data_offset + byte_size, '\0')); 542 data.SetData(data_sp); 543 } 544 545 uint8_t *dst = const_cast<uint8_t *>(data.PeekData(data_offset, byte_size)); 546 if (dst != NULL) { 547 if (address_type == eAddressTypeHost) { 548 // The address is an address in this process, so just copy it. 549 if (address == 0) { 550 error.SetErrorStringWithFormat( 551 "trying to read from host address of 0."); 552 return error; 553 } 554 memcpy(dst, (uint8_t *)NULL + address, byte_size); 555 } else if ((address_type == eAddressTypeLoad) || 556 (address_type == eAddressTypeFile)) { 557 if (file_so_addr.IsValid()) { 558 // We have a file address that we were able to translate into a 559 // section offset address so we might be able to read this from 560 // the object files if we don't have a live process. Lets always 561 // try and read from the process if we have one though since we 562 // want to read the actual value by setting "prefer_file_cache" 563 // to false. 564 const bool prefer_file_cache = false; 565 if (exe_ctx->GetTargetRef().ReadMemory(file_so_addr, prefer_file_cache, 566 dst, byte_size, 567 error) != byte_size) { 568 error.SetErrorStringWithFormat( 569 "read memory from 0x%" PRIx64 " failed", (uint64_t)address); 570 } 571 } else { 572 // The execution context might have a NULL process, but it 573 // might have a valid process in the exe_ctx->target, so use 574 // the ExecutionContext::GetProcess accessor to ensure we 575 // get the process if there is one. 576 Process *process = exe_ctx->GetProcessPtr(); 577 578 if (process) { 579 const size_t bytes_read = 580 process->ReadMemory(address, dst, byte_size, error); 581 if (bytes_read != byte_size) 582 error.SetErrorStringWithFormat( 583 "read memory from 0x%" PRIx64 " failed (%u of %u bytes read)", 584 (uint64_t)address, (uint32_t)bytes_read, (uint32_t)byte_size); 585 } else { 586 error.SetErrorStringWithFormat("read memory from 0x%" PRIx64 587 " failed (invalid process)", 588 (uint64_t)address); 589 } 590 } 591 } else { 592 error.SetErrorStringWithFormat("unsupported AddressType value (%i)", 593 address_type); 594 } 595 } else { 596 error.SetErrorStringWithFormat("out of memory"); 597 } 598 599 return error; 600 } 601 602 Scalar &Value::ResolveValue(ExecutionContext *exe_ctx) { 603 const CompilerType &compiler_type = GetCompilerType(); 604 if (compiler_type.IsValid()) { 605 switch (m_value_type) { 606 case eValueTypeScalar: // raw scalar value 607 break; 608 609 default: 610 case eValueTypeFileAddress: 611 case eValueTypeLoadAddress: // load address value 612 case eValueTypeHostAddress: // host address value (for memory in the process 613 // that is using liblldb) 614 { 615 DataExtractor data; 616 lldb::addr_t addr = m_value.ULongLong(LLDB_INVALID_ADDRESS); 617 Error error(GetValueAsData(exe_ctx, data, 0, NULL)); 618 if (error.Success()) { 619 Scalar scalar; 620 if (compiler_type.GetValueAsScalar(data, 0, data.GetByteSize(), 621 scalar)) { 622 m_value = scalar; 623 m_value_type = eValueTypeScalar; 624 } else { 625 if ((uintptr_t)addr != (uintptr_t)m_data_buffer.GetBytes()) { 626 m_value.Clear(); 627 m_value_type = eValueTypeScalar; 628 } 629 } 630 } else { 631 if ((uintptr_t)addr != (uintptr_t)m_data_buffer.GetBytes()) { 632 m_value.Clear(); 633 m_value_type = eValueTypeScalar; 634 } 635 } 636 } break; 637 } 638 } 639 return m_value; 640 } 641 642 Variable *Value::GetVariable() { 643 if (m_context_type == eContextTypeVariable) 644 return static_cast<Variable *>(m_context); 645 return NULL; 646 } 647 648 void Value::Clear() { 649 m_value.Clear(); 650 m_vector.Clear(); 651 m_compiler_type.Clear(); 652 m_value_type = eValueTypeScalar; 653 m_context = NULL; 654 m_context_type = eContextTypeInvalid; 655 m_data_buffer.Clear(); 656 } 657 658 const char *Value::GetValueTypeAsCString(ValueType value_type) { 659 switch (value_type) { 660 case eValueTypeScalar: 661 return "scalar"; 662 case eValueTypeVector: 663 return "vector"; 664 case eValueTypeFileAddress: 665 return "file address"; 666 case eValueTypeLoadAddress: 667 return "load address"; 668 case eValueTypeHostAddress: 669 return "host address"; 670 }; 671 return "???"; 672 } 673 674 const char *Value::GetContextTypeAsCString(ContextType context_type) { 675 switch (context_type) { 676 case eContextTypeInvalid: 677 return "invalid"; 678 case eContextTypeRegisterInfo: 679 return "RegisterInfo *"; 680 case eContextTypeLLDBType: 681 return "Type *"; 682 case eContextTypeVariable: 683 return "Variable *"; 684 }; 685 return "???"; 686 } 687 688 ValueList::ValueList(const ValueList &rhs) { m_values = rhs.m_values; } 689 690 const ValueList &ValueList::operator=(const ValueList &rhs) { 691 m_values = rhs.m_values; 692 return *this; 693 } 694 695 void ValueList::PushValue(const Value &value) { m_values.push_back(value); } 696 697 size_t ValueList::GetSize() { return m_values.size(); } 698 699 Value *ValueList::GetValueAtIndex(size_t idx) { 700 if (idx < GetSize()) { 701 return &(m_values[idx]); 702 } else 703 return NULL; 704 } 705 706 void ValueList::Clear() { m_values.clear(); } 707