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