1 //===-- Type.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 // Other libraries and framework includes
11 
12 #include "lldb/Core/DataExtractor.h"
13 #include "lldb/Core/DataBufferHeap.h"
14 #include "lldb/Core/Module.h"
15 #include "lldb/Core/Scalar.h"
16 #include "lldb/Core/StreamString.h"
17 
18 #include "lldb/Symbol/ClangASTType.h"
19 #include "lldb/Symbol/ClangASTContext.h"
20 #include "lldb/Symbol/ObjectFile.h"
21 #include "lldb/Symbol/SymbolContextScope.h"
22 #include "lldb/Symbol/SymbolFile.h"
23 #include "lldb/Symbol/Type.h"
24 #include "lldb/Symbol/TypeList.h"
25 
26 #include "lldb/Target/ExecutionContext.h"
27 #include "lldb/Target/Process.h"
28 #include "lldb/Target/Target.h"
29 
30 using namespace lldb;
31 using namespace lldb_private;
32 
33 Type::Type
34 (
35     lldb::user_id_t uid,
36     SymbolFile* symbol_file,
37     const ConstString &name,
38     uint32_t byte_size,
39     SymbolContextScope *context,
40     user_id_t encoding_uid,
41     EncodingDataType encoding_uid_type,
42     const Declaration& decl,
43     clang_type_t clang_type,
44     ResolveState clang_type_resolve_state
45 ) :
46     UserID (uid),
47     m_name (name),
48     m_symbol_file (symbol_file),
49     m_context (context),
50     m_encoding_type (NULL),
51     m_encoding_uid (encoding_uid),
52     m_encoding_uid_type (encoding_uid_type),
53     m_byte_size (byte_size),
54     m_decl (decl),
55     m_clang_type (clang_type),
56     m_clang_type_resolve_state (clang_type ? clang_type_resolve_state : eResolveStateUnresolved)
57 {
58 }
59 
60 Type::Type () :
61     UserID (0),
62     m_name ("<INVALID TYPE>"),
63     m_symbol_file (NULL),
64     m_context (NULL),
65     m_encoding_type (NULL),
66     m_encoding_uid (0),
67     m_encoding_uid_type (eEncodingInvalid),
68     m_byte_size (0),
69     m_decl (),
70     m_clang_type (NULL),
71     m_clang_type_resolve_state (eResolveStateUnresolved)
72 {
73 }
74 
75 
76 Type::Type (const Type &rhs) :
77     UserID (rhs),
78     m_name (rhs.m_name),
79     m_symbol_file (rhs.m_symbol_file),
80     m_context (rhs.m_context),
81     m_encoding_type (rhs.m_encoding_type),
82     m_encoding_uid (rhs.m_encoding_uid),
83     m_encoding_uid_type (rhs.m_encoding_uid_type),
84     m_byte_size (rhs.m_byte_size),
85     m_decl (rhs.m_decl),
86     m_clang_type (rhs.m_clang_type),
87     m_clang_type_resolve_state (rhs.m_clang_type_resolve_state)
88 {
89 }
90 
91 const Type&
92 Type::operator= (const Type& rhs)
93 {
94     if (this != &rhs)
95     {
96     }
97     return *this;
98 }
99 
100 
101 void
102 Type::GetDescription (Stream *s, lldb::DescriptionLevel level, bool show_name)
103 {
104     *s << "id = " << (const UserID&)*this;
105 
106     // Call the name accessor to make sure we resolve the type name
107     if (show_name && GetName())
108         *s << ", name = \"" << m_name << '"';
109 
110     // Call the get byte size accesor so we resolve our byte size
111     if (GetByteSize())
112         s->Printf(", byte-size = %zu", m_byte_size);
113     bool show_fullpaths = (level == lldb::eDescriptionLevelVerbose);
114     m_decl.Dump(s, show_fullpaths);
115 
116     if (m_clang_type)
117     {
118         *s << ", clang_type = \"";
119         ClangASTType::DumpTypeDescription (GetClangAST(), m_clang_type, s);
120         *s << '"';
121     }
122     else if (m_encoding_uid != LLDB_INVALID_UID)
123     {
124         s->Printf(", type_uid = 0x%8.8x", m_encoding_uid);
125         switch (m_encoding_uid_type)
126         {
127         case eEncodingInvalid: break;
128         case eEncodingIsUID: s->PutCString(" (unresolved type)"); break;
129         case eEncodingIsConstUID: s->PutCString(" (unresolved const type)"); break;
130         case eEncodingIsRestrictUID: s->PutCString(" (unresolved restrict type)"); break;
131         case eEncodingIsVolatileUID: s->PutCString(" (unresolved volatile type)"); break;
132         case eEncodingIsTypedefUID: s->PutCString(" (unresolved typedef)"); break;
133         case eEncodingIsPointerUID: s->PutCString(" (unresolved pointer)"); break;
134         case eEncodingIsLValueReferenceUID: s->PutCString(" (unresolved L value reference)"); break;
135         case eEncodingIsRValueReferenceUID: s->PutCString(" (unresolved R value reference)"); break;
136         case eEncodingIsSyntheticUID: s->PutCString(" (synthetic type)"); break;
137         }
138     }
139 }
140 
141 
142 void
143 Type::Dump (Stream *s, bool show_context)
144 {
145     s->Printf("%p: ", this);
146     s->Indent();
147     *s << "Type" << (const UserID&)*this << ' ';
148     if (m_name)
149         *s << ", name = \"" << m_name << "\"";
150 
151     if (m_byte_size != 0)
152         s->Printf(", size = %zu", m_byte_size);
153 
154     if (show_context && m_context != NULL)
155     {
156         s->PutCString(", context = ( ");
157         m_context->DumpSymbolContext(s);
158         s->PutCString(" )");
159     }
160 
161     bool show_fullpaths = false;
162     m_decl.Dump (s,show_fullpaths);
163 
164     if (m_clang_type)
165     {
166         *s << ", clang_type = " << m_clang_type << ' ';
167 
168         ClangASTType::DumpTypeDescription (GetClangAST(), m_clang_type, s);
169     }
170     else if (m_encoding_uid != LLDB_INVALID_UID)
171     {
172         *s << ", type_data = " << (uint64_t)m_encoding_uid;
173         switch (m_encoding_uid_type)
174         {
175         case eEncodingInvalid: break;
176         case eEncodingIsUID: s->PutCString(" (unresolved type)"); break;
177         case eEncodingIsConstUID: s->PutCString(" (unresolved const type)"); break;
178         case eEncodingIsRestrictUID: s->PutCString(" (unresolved restrict type)"); break;
179         case eEncodingIsVolatileUID: s->PutCString(" (unresolved volatile type)"); break;
180         case eEncodingIsTypedefUID: s->PutCString(" (unresolved typedef)"); break;
181         case eEncodingIsPointerUID: s->PutCString(" (unresolved pointer)"); break;
182         case eEncodingIsLValueReferenceUID: s->PutCString(" (unresolved L value reference)"); break;
183         case eEncodingIsRValueReferenceUID: s->PutCString(" (unresolved R value reference)"); break;
184         case eEncodingIsSyntheticUID: s->PutCString(" (synthetic type)"); break;
185         }
186     }
187 
188 //
189 //  if (m_access)
190 //      s->Printf(", access = %u", m_access);
191     s->EOL();
192 }
193 
194 const ConstString &
195 Type::GetName()
196 {
197     if (!m_name)
198     {
199         if (ResolveClangType(eResolveStateForward))
200             m_name = ClangASTType::GetConstTypeName (m_clang_type);
201     }
202     return m_name;
203 }
204 
205 void
206 Type::DumpTypeName(Stream *s)
207 {
208     GetName().Dump(s, "<invalid-type-name>");
209 }
210 
211 
212 void
213 Type::DumpValue
214 (
215     ExecutionContext *exe_ctx,
216     Stream *s,
217     const DataExtractor &data,
218     uint32_t data_byte_offset,
219     bool show_types,
220     bool show_summary,
221     bool verbose,
222     lldb::Format format
223 )
224 {
225     if (ResolveClangType(eResolveStateForward))
226     {
227         if (show_types)
228         {
229             s->PutChar('(');
230             if (verbose)
231                 s->Printf("Type{0x%8.8x} ", GetID());
232             DumpTypeName (s);
233             s->PutCString(") ");
234         }
235 
236         ClangASTType::DumpValue (GetClangAST (),
237                                                m_clang_type,
238                                                exe_ctx,
239                                                s,
240                                                format == lldb::eFormatDefault ? GetFormat() : format,
241                                                data,
242                                                data_byte_offset,
243                                                GetByteSize(),
244                                                0, // Bitfield bit size
245                                                0, // Bitfield bit offset
246                                                show_types,
247                                                show_summary,
248                                                verbose,
249                                                0);
250     }
251 }
252 
253 Type *
254 Type::GetEncodingType ()
255 {
256     if (m_encoding_type == NULL && m_encoding_uid != LLDB_INVALID_UID)
257         m_encoding_type = m_symbol_file->ResolveTypeUID(m_encoding_uid);
258     return m_encoding_type;
259 }
260 
261 
262 
263 uint32_t
264 Type::GetByteSize()
265 {
266     if (m_byte_size == 0)
267     {
268         switch (m_encoding_uid_type)
269         {
270         case eEncodingInvalid:
271         case eEncodingIsSyntheticUID:
272             break;
273         case eEncodingIsUID:
274         case eEncodingIsConstUID:
275         case eEncodingIsRestrictUID:
276         case eEncodingIsVolatileUID:
277         case eEncodingIsTypedefUID:
278             {
279                 Type *encoding_type = GetEncodingType ();
280                 if (encoding_type)
281                     m_byte_size = encoding_type->GetByteSize();
282                 if (m_byte_size == 0)
283                 {
284                     uint32_t bit_width = ClangASTType::GetClangTypeBitWidth (GetClangAST(), GetClangLayoutType());
285                     m_byte_size = (bit_width + 7 ) / 8;
286                 }
287             }
288             break;
289 
290         // If we are a pointer or reference, then this is just a pointer size;
291         case eEncodingIsPointerUID:
292         case eEncodingIsLValueReferenceUID:
293         case eEncodingIsRValueReferenceUID:
294             m_byte_size = m_symbol_file->GetClangASTContext().GetPointerBitSize() / 8;
295             break;
296         }
297     }
298     return m_byte_size;
299 }
300 
301 
302 uint32_t
303 Type::GetNumChildren (bool omit_empty_base_classes)
304 {
305     if (ResolveClangType(eResolveStateForward))
306     {
307         return ClangASTContext::GetNumChildren (m_symbol_file->GetClangASTContext().getASTContext(),
308                                                 m_clang_type,
309                                                 omit_empty_base_classes);
310     }
311     return 0;
312 }
313 
314 bool
315 Type::IsAggregateType ()
316 {
317     if (ResolveClangType(eResolveStateForward))
318         return ClangASTContext::IsAggregateType (m_clang_type);
319     return false;
320 }
321 
322 lldb::Format
323 Type::GetFormat ()
324 {
325     // Make sure we resolve our type if it already hasn't been.
326     if (!ResolveClangType(eResolveStateForward))
327         return lldb::eFormatInvalid;
328     return ClangASTType::GetFormat (m_clang_type);
329 }
330 
331 
332 
333 lldb::Encoding
334 Type::GetEncoding (uint32_t &count)
335 {
336     // Make sure we resolve our type if it already hasn't been.
337     if (!ResolveClangType(eResolveStateForward))
338         return lldb::eEncodingInvalid;
339 
340     return ClangASTType::GetEncoding (m_clang_type, count);
341 }
342 
343 
344 
345 bool
346 Type::DumpValueInMemory
347 (
348     ExecutionContext *exe_ctx,
349     Stream *s,
350     lldb::addr_t address,
351     AddressType address_type,
352     bool show_types,
353     bool show_summary,
354     bool verbose
355 )
356 {
357     if (address != LLDB_INVALID_ADDRESS)
358     {
359         DataExtractor data;
360         Target *target = NULL;
361         if (exe_ctx)
362             target = exe_ctx->GetTargetPtr();
363         if (target)
364             data.SetByteOrder (target->GetArchitecture().GetByteOrder());
365         if (ReadFromMemory (exe_ctx, address, address_type, data))
366         {
367             DumpValue(exe_ctx, s, data, 0, show_types, show_summary, verbose);
368             return true;
369         }
370     }
371     return false;
372 }
373 
374 
375 bool
376 Type::ReadFromMemory (ExecutionContext *exe_ctx, lldb::addr_t addr, AddressType address_type, DataExtractor &data)
377 {
378     if (address_type == eAddressTypeFile)
379     {
380         // Can't convert a file address to anything valid without more
381         // context (which Module it came from)
382         return false;
383     }
384 
385     const uint32_t byte_size = GetByteSize();
386     if (data.GetByteSize() < byte_size)
387     {
388         lldb::DataBufferSP data_sp(new DataBufferHeap (byte_size, '\0'));
389         data.SetData(data_sp);
390     }
391 
392     uint8_t* dst = (uint8_t*)data.PeekData(0, byte_size);
393     if (dst != NULL)
394     {
395         if (address_type == eAddressTypeHost)
396         {
397             // The address is an address in this process, so just copy it
398             memcpy (dst, (uint8_t*)NULL + addr, byte_size);
399             return true;
400         }
401         else
402         {
403             if (exe_ctx)
404             {
405                 Process *process = exe_ctx->GetProcessPtr();
406                 if (process)
407                 {
408                     Error error;
409                     return exe_ctx->GetProcessPtr()->ReadMemory(addr, dst, byte_size, error) == byte_size;
410                 }
411             }
412         }
413     }
414     return false;
415 }
416 
417 
418 bool
419 Type::WriteToMemory (ExecutionContext *exe_ctx, lldb::addr_t addr, AddressType address_type, DataExtractor &data)
420 {
421     return false;
422 }
423 
424 
425 TypeList*
426 Type::GetTypeList()
427 {
428     return GetSymbolFile()->GetTypeList();
429 }
430 
431 const Declaration &
432 Type::GetDeclaration () const
433 {
434     return m_decl;
435 }
436 
437 bool
438 Type::ResolveClangType (ResolveState clang_type_resolve_state)
439 {
440     Type *encoding_type = NULL;
441     if (m_clang_type == NULL)
442     {
443         encoding_type = GetEncodingType();
444         if (encoding_type)
445         {
446             switch (m_encoding_uid_type)
447             {
448             case eEncodingIsUID:
449                 if (encoding_type->ResolveClangType(clang_type_resolve_state))
450                 {
451                     m_clang_type = encoding_type->m_clang_type;
452                     m_clang_type_resolve_state = encoding_type->m_clang_type_resolve_state;
453                 }
454                 break;
455 
456             case eEncodingIsConstUID:
457                 m_clang_type = ClangASTContext::AddConstModifier (encoding_type->GetClangForwardType());
458                 break;
459 
460             case eEncodingIsRestrictUID:
461                 m_clang_type = ClangASTContext::AddRestrictModifier (encoding_type->GetClangForwardType());
462                 break;
463 
464             case eEncodingIsVolatileUID:
465                 m_clang_type = ClangASTContext::AddVolatileModifier (encoding_type->GetClangForwardType());
466                 break;
467 
468             case eEncodingIsTypedefUID:
469                 m_clang_type = CreateClangTypedefType (this, encoding_type);
470                 // Clear the name so it can get fully qualified in case the
471                 // typedef is in a namespace.
472                 m_name.Clear();
473                 break;
474 
475             case eEncodingIsPointerUID:
476                 m_clang_type = CreateClangPointerType (encoding_type);
477                 break;
478 
479             case eEncodingIsLValueReferenceUID:
480                 m_clang_type = CreateClangLValueReferenceType (encoding_type);
481                 break;
482 
483             case eEncodingIsRValueReferenceUID:
484                 m_clang_type = CreateClangRValueReferenceType (encoding_type);
485                 break;
486 
487             default:
488                 assert(!"Unhandled encoding_data_type.");
489                 break;
490             }
491         }
492         else
493         {
494             // We have no encoding type, return void?
495             clang_type_t void_clang_type = GetClangASTContext().GetBuiltInType_void();
496             switch (m_encoding_uid_type)
497             {
498             case eEncodingIsUID:
499                 m_clang_type = void_clang_type;
500                 break;
501 
502             case eEncodingIsConstUID:
503                 m_clang_type = ClangASTContext::AddConstModifier (void_clang_type);
504                 break;
505 
506             case eEncodingIsRestrictUID:
507                 m_clang_type = ClangASTContext::AddRestrictModifier (void_clang_type);
508                 break;
509 
510             case eEncodingIsVolatileUID:
511                 m_clang_type = ClangASTContext::AddVolatileModifier (void_clang_type);
512                 break;
513 
514             case eEncodingIsTypedefUID:
515                 m_clang_type = GetClangASTContext().CreateTypedefType (m_name.AsCString(), void_clang_type, NULL);
516                 break;
517 
518             case eEncodingIsPointerUID:
519                 m_clang_type = GetClangASTContext().CreatePointerType (void_clang_type);
520                 break;
521 
522             case eEncodingIsLValueReferenceUID:
523                 m_clang_type = GetClangASTContext().CreateLValueReferenceType (void_clang_type);
524                 break;
525 
526             case eEncodingIsRValueReferenceUID:
527                 m_clang_type = GetClangASTContext().CreateRValueReferenceType (void_clang_type);
528                 break;
529 
530             default:
531                 assert(!"Unhandled encoding_data_type.");
532                 break;
533             }
534         }
535     }
536 
537     // Check if we have a forward reference to a class/struct/union/enum?
538     if (m_clang_type && m_clang_type_resolve_state < clang_type_resolve_state)
539     {
540         m_clang_type_resolve_state = eResolveStateFull;
541         if (!ClangASTType::IsDefined (m_clang_type))
542         {
543             // We have a forward declaration, we need to resolve it to a complete
544             // definition.
545             m_symbol_file->ResolveClangOpaqueTypeDefinition (m_clang_type);
546         }
547     }
548 
549     // If we have an encoding type, then we need to make sure it is
550     // resolved appropriately.
551     if (m_encoding_uid != LLDB_INVALID_UID)
552     {
553         if (encoding_type == NULL)
554             encoding_type = GetEncodingType();
555         if (encoding_type)
556         {
557             ResolveState encoding_clang_type_resolve_state = clang_type_resolve_state;
558 
559             if (clang_type_resolve_state == eResolveStateLayout)
560             {
561                 switch (m_encoding_uid_type)
562                 {
563                 case eEncodingIsPointerUID:
564                 case eEncodingIsLValueReferenceUID:
565                 case eEncodingIsRValueReferenceUID:
566                     encoding_clang_type_resolve_state = eResolveStateForward;
567                     break;
568                 default:
569                     break;
570                 }
571             }
572             encoding_type->ResolveClangType (encoding_clang_type_resolve_state);
573         }
574     }
575     return m_clang_type != NULL;
576 }
577 uint32_t
578 Type::GetEncodingMask ()
579 {
580     uint32_t encoding_mask = 1u << m_encoding_uid_type;
581     Type *encoding_type = GetEncodingType();
582     assert (encoding_type != this);
583     if (encoding_type)
584         encoding_mask |= encoding_type->GetEncodingMask ();
585     return encoding_mask;
586 }
587 
588 clang_type_t
589 Type::GetClangFullType ()
590 {
591     ResolveClangType(eResolveStateFull);
592     return m_clang_type;
593 }
594 
595 clang_type_t
596 Type::GetClangLayoutType ()
597 {
598     ResolveClangType(eResolveStateLayout);
599     return m_clang_type;
600 }
601 
602 clang_type_t
603 Type::GetClangForwardType ()
604 {
605     ResolveClangType (eResolveStateForward);
606     return m_clang_type;
607 }
608 
609 clang::ASTContext *
610 Type::GetClangAST ()
611 {
612     return GetClangASTContext().getASTContext();
613 }
614 
615 ClangASTContext &
616 Type::GetClangASTContext ()
617 {
618     return m_symbol_file->GetClangASTContext();
619 }
620 
621 int
622 Type::Compare(const Type &a, const Type &b)
623 {
624     // Just compare the UID values for now...
625     lldb::user_id_t a_uid = a.GetID();
626     lldb::user_id_t b_uid = b.GetID();
627     if (a_uid < b_uid)
628         return -1;
629     if (a_uid > b_uid)
630         return 1;
631     return 0;
632 //  if (a.getQualType() == b.getQualType())
633 //      return 0;
634 }
635 
636 
637 void *
638 Type::CreateClangPointerType (Type *type)
639 {
640     assert(type);
641     return GetClangASTContext().CreatePointerType(type->GetClangForwardType());
642 }
643 
644 void *
645 Type::CreateClangTypedefType (Type *typedef_type, Type *base_type)
646 {
647     assert(typedef_type && base_type);
648     return GetClangASTContext().CreateTypedefType (typedef_type->GetName().AsCString(),
649                                                    base_type->GetClangForwardType(),
650                                                    typedef_type->GetSymbolFile()->GetClangDeclContextContainingTypeUID(typedef_type->GetID()));
651 }
652 
653 void *
654 Type::CreateClangLValueReferenceType (Type *type)
655 {
656     assert(type);
657     return GetClangASTContext().CreateLValueReferenceType(type->GetClangForwardType());
658 }
659 
660 void *
661 Type::CreateClangRValueReferenceType (Type *type)
662 {
663     assert(type);
664     return GetClangASTContext().CreateRValueReferenceType (type->GetClangForwardType());
665 }
666 
667 
668 TypeAndOrName::TypeAndOrName () : m_type_sp(), m_type_name()
669 {
670 
671 }
672 
673 TypeAndOrName::TypeAndOrName (TypeSP &in_type_sp) : m_type_sp(in_type_sp)
674 {
675     if (in_type_sp)
676         m_type_name = in_type_sp->GetName();
677 }
678 
679 TypeAndOrName::TypeAndOrName (const char *in_type_str) : m_type_name(in_type_str)
680 {
681 }
682 
683 TypeAndOrName::TypeAndOrName (const TypeAndOrName &rhs) : m_type_sp (rhs.m_type_sp), m_type_name (rhs.m_type_name)
684 {
685 
686 }
687 
688 TypeAndOrName::TypeAndOrName (ConstString &in_type_const_string) : m_type_name (in_type_const_string)
689 {
690 }
691 
692 TypeAndOrName &
693 TypeAndOrName::operator= (const TypeAndOrName &rhs)
694 {
695     if (this != &rhs)
696     {
697         m_type_name = rhs.m_type_name;
698         m_type_sp = rhs.m_type_sp;
699     }
700     return *this;
701 }
702 
703 ConstString
704 TypeAndOrName::GetName () const
705 {
706     if (m_type_sp)
707         return m_type_sp->GetName();
708     else
709         return m_type_name;
710 }
711 
712 void
713 TypeAndOrName::SetName (ConstString &type_name_const_str)
714 {
715     m_type_name = type_name_const_str;
716 }
717 
718 void
719 TypeAndOrName::SetName (const char *type_name_str)
720 {
721     m_type_name.SetCString (type_name_str);
722 }
723 
724 void
725 TypeAndOrName::SetTypeSP (lldb::TypeSP type_sp)
726 {
727     m_type_sp = type_sp;
728     if (type_sp)
729         m_type_name = type_sp->GetName();
730 }
731 
732 bool
733 TypeAndOrName::IsEmpty()
734 {
735     if (m_type_name || m_type_sp)
736         return false;
737     else
738         return true;
739 }
740 
741 TypeImpl::TypeImpl(const lldb_private::ClangASTType& clang_ast_type) :
742     m_clang_ast_type(clang_ast_type.GetASTContext(), clang_ast_type.GetOpaqueQualType()),
743     m_type_sp()
744 {}
745 
746 TypeImpl::TypeImpl(const lldb::TypeSP& type) :
747     m_clang_ast_type(type->GetClangAST(), type->GetClangFullType()),
748     m_type_sp(type)
749 {
750 }
751 
752 TypeImpl&
753 TypeImpl::operator = (const TypeImpl& rhs)
754 {
755     if (*this != rhs)
756     {
757         m_clang_ast_type = rhs.m_clang_ast_type;
758         m_type_sp = rhs.m_type_sp;
759     }
760     return *this;
761 }
762 
763 clang::ASTContext*
764 TypeImpl::GetASTContext()
765 {
766     if (!IsValid())
767         return NULL;
768 
769     return m_clang_ast_type.GetASTContext();
770 }
771 
772 lldb::clang_type_t
773 TypeImpl::GetOpaqueQualType()
774 {
775     if (!IsValid())
776         return NULL;
777 
778     return m_clang_ast_type.GetOpaqueQualType();
779 }
780