1 //===-- StackFrame.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/Target/StackFrame.h"
11 
12 // C Includes
13 // C++ Includes
14 // Other libraries and framework includes
15 // Project includes
16 #include "lldb/Core/Module.h"
17 #include "lldb/Core/Disassembler.h"
18 #include "lldb/Core/Value.h"
19 #include "lldb/Core/ValueObjectVariable.h"
20 #include "lldb/Symbol/Function.h"
21 #include "lldb/Symbol/VariableList.h"
22 #include "lldb/Target/ExecutionContext.h"
23 #include "lldb/Target/Process.h"
24 #include "lldb/Target/RegisterContext.h"
25 #include "lldb/Target/Target.h"
26 #include "lldb/Target/Thread.h"
27 
28 using namespace lldb;
29 using namespace lldb_private;
30 
31 // The first bits in the flags are reserved for the SymbolContext::Scope bits
32 // so we know if we have tried to look up information in our internal symbol
33 // context (m_sc) already.
34 #define RESOLVED_FRAME_CODE_ADDR        (uint32_t(eSymbolContextEverything + 1))
35 #define RESOLVED_FRAME_ID_SYMBOL_SCOPE  (RESOLVED_FRAME_CODE_ADDR << 1)
36 #define GOT_FRAME_BASE                  (RESOLVED_FRAME_ID_SYMBOL_SCOPE << 1)
37 #define RESOLVED_VARIABLES              (GOT_FRAME_BASE << 1)
38 
39 StackFrame::StackFrame
40 (
41     lldb::user_id_t frame_idx,
42     lldb::user_id_t unwind_frame_index,
43     Thread &thread,
44     lldb::addr_t cfa,
45     lldb::addr_t pc,
46     const SymbolContext *sc_ptr
47 ) :
48     m_frame_index (frame_idx),
49     m_unwind_frame_index (unwind_frame_index),
50     m_thread (thread),
51     m_reg_context_sp (),
52     m_id (pc, cfa, NULL),
53     m_frame_code_addr (NULL, pc),
54     m_sc (),
55     m_flags (),
56     m_frame_base (),
57     m_frame_base_error (),
58     m_variable_list_sp (),
59     m_variable_list_value_objects ()
60 {
61     if (sc_ptr != NULL)
62     {
63         m_sc = *sc_ptr;
64         m_flags.Set(m_sc.GetResolvedMask ());
65     }
66 }
67 
68 StackFrame::StackFrame
69 (
70     lldb::user_id_t frame_idx,
71     lldb::user_id_t unwind_frame_index,
72     Thread &thread,
73     const RegisterContextSP &reg_context_sp,
74     lldb::addr_t cfa,
75     lldb::addr_t pc,
76     const SymbolContext *sc_ptr
77 ) :
78     m_frame_index (frame_idx),
79     m_unwind_frame_index (unwind_frame_index),
80     m_thread (thread),
81     m_reg_context_sp (reg_context_sp),
82     m_id (pc, cfa, NULL),
83     m_frame_code_addr (NULL, pc),
84     m_sc (),
85     m_flags (),
86     m_frame_base (),
87     m_frame_base_error (),
88     m_variable_list_sp (),
89     m_variable_list_value_objects ()
90 {
91     if (sc_ptr != NULL)
92     {
93         m_sc = *sc_ptr;
94         m_flags.Set(m_sc.GetResolvedMask ());
95     }
96 
97     if (reg_context_sp && !m_sc.target_sp)
98     {
99         m_sc.target_sp = reg_context_sp->GetThread().GetProcess().GetTarget().GetSP();
100         m_flags.Set (eSymbolContextTarget);
101     }
102 }
103 
104 StackFrame::StackFrame
105 (
106     lldb::user_id_t frame_idx,
107     lldb::user_id_t unwind_frame_index,
108     Thread &thread,
109     const RegisterContextSP &reg_context_sp,
110     lldb::addr_t cfa,
111     const Address& pc_addr,
112     const SymbolContext *sc_ptr
113 ) :
114     m_frame_index (frame_idx),
115     m_unwind_frame_index (unwind_frame_index),
116     m_thread (thread),
117     m_reg_context_sp (reg_context_sp),
118     m_id (pc_addr.GetLoadAddress (&thread.GetProcess().GetTarget()), cfa, NULL),
119     m_frame_code_addr (pc_addr),
120     m_sc (),
121     m_flags (),
122     m_frame_base (),
123     m_frame_base_error (),
124     m_variable_list_sp (),
125     m_variable_list_value_objects ()
126 {
127     if (sc_ptr != NULL)
128     {
129         m_sc = *sc_ptr;
130         m_flags.Set(m_sc.GetResolvedMask ());
131     }
132 
133     if (m_sc.target_sp.get() == NULL && reg_context_sp)
134     {
135         m_sc.target_sp = reg_context_sp->GetThread().GetProcess().GetTarget().GetSP();
136         m_flags.Set (eSymbolContextTarget);
137     }
138 
139     Module *pc_module = pc_addr.GetModule();
140     if (m_sc.module_sp.get() == NULL || m_sc.module_sp.get() != pc_module)
141     {
142         if (pc_module)
143         {
144             m_sc.module_sp = pc_module->GetSP();
145             m_flags.Set (eSymbolContextModule);
146         }
147         else
148         {
149             m_sc.module_sp.reset();
150         }
151 
152     }
153 }
154 
155 
156 //----------------------------------------------------------------------
157 // Destructor
158 //----------------------------------------------------------------------
159 StackFrame::~StackFrame()
160 {
161 }
162 
163 StackID&
164 StackFrame::GetStackID()
165 {
166     // Make sure we have resolved the StackID object's symbol context scope if
167     // we already haven't looked it up.
168 
169     if (m_flags.IsClear (RESOLVED_FRAME_ID_SYMBOL_SCOPE))
170     {
171         if (m_id.GetSymbolContextScope ())
172         {
173             // We already have a symbol context scope, we just don't have our
174             // flag bit set.
175             m_flags.Set (RESOLVED_FRAME_ID_SYMBOL_SCOPE);
176         }
177         else
178         {
179             // Calculate the frame block and use this for the stack ID symbol
180             // context scope if we have one.
181             SymbolContextScope *scope = GetFrameBlock ();
182             if (scope == NULL)
183             {
184                 // We don't have a block, so use the symbol
185                 if (m_flags.IsClear (eSymbolContextSymbol))
186                     GetSymbolContext (eSymbolContextSymbol);
187 
188                 // It is ok if m_sc.symbol is NULL here
189                 scope = m_sc.symbol;
190             }
191             // Set the symbol context scope (the accessor will set the
192             // RESOLVED_FRAME_ID_SYMBOL_SCOPE bit in m_flags).
193             SetSymbolContextScope (scope);
194         }
195     }
196     return m_id;
197 }
198 
199 void
200 StackFrame::SetSymbolContextScope (SymbolContextScope *symbol_scope)
201 {
202     m_flags.Set (RESOLVED_FRAME_ID_SYMBOL_SCOPE);
203     m_id.SetSymbolContextScope (symbol_scope);
204 }
205 
206 Address&
207 StackFrame::GetFrameCodeAddress()
208 {
209     if (m_flags.IsClear(RESOLVED_FRAME_CODE_ADDR) && !m_frame_code_addr.IsSectionOffset())
210     {
211         m_flags.Set (RESOLVED_FRAME_CODE_ADDR);
212 
213         // Resolve the PC into a temporary address because if ResolveLoadAddress
214         // fails to resolve the address, it will clear the address object...
215         Address resolved_pc;
216         if (m_thread.GetProcess().GetTarget().GetSectionLoadList().ResolveLoadAddress(m_frame_code_addr.GetOffset(), resolved_pc))
217         {
218             m_frame_code_addr = resolved_pc;
219             const Section *section = m_frame_code_addr.GetSection();
220             if (section)
221             {
222                 Module *module = section->GetModule();
223                 if (module)
224                 {
225                     m_sc.module_sp = module->GetSP();
226                     if (m_sc.module_sp)
227                         m_flags.Set(eSymbolContextModule);
228                 }
229             }
230         }
231     }
232     return m_frame_code_addr;
233 }
234 
235 void
236 StackFrame::ChangePC (addr_t pc)
237 {
238     m_frame_code_addr.SetOffset(pc);
239     m_frame_code_addr.SetSection(NULL);
240     m_sc.Clear();
241     m_flags.SetAllFlagBits(0);
242     m_thread.ClearStackFrames ();
243 }
244 
245 const char *
246 StackFrame::Disassemble ()
247 {
248     if (m_disassembly.GetSize() == 0)
249     {
250         ExecutionContext exe_ctx;
251         Calculate(exe_ctx);
252         Target &target = m_thread.GetProcess().GetTarget();
253         Disassembler::Disassemble (target.GetDebugger(),
254                                    target.GetArchitecture(),
255                                    exe_ctx,
256                                    0,
257                                    false,
258                                    m_disassembly);
259         if (m_disassembly.GetSize() == 0)
260             return NULL;
261     }
262     return m_disassembly.GetData();
263 }
264 
265 Block *
266 StackFrame::GetFrameBlock ()
267 {
268     if (m_sc.block == NULL && m_flags.IsClear (eSymbolContextBlock))
269         GetSymbolContext (eSymbolContextBlock);
270 
271     if (m_sc.block)
272     {
273         Block *inline_block = m_sc.block->GetContainingInlinedBlock();
274         if (inline_block)
275         {
276             // Use the block with the inlined function info
277             // as the frame block we want this frame to have only the variables
278             // for the inlined function and its non-inlined block child blocks.
279             return inline_block;
280         }
281         else
282         {
283             // This block is not contained withing any inlined function blocks
284             // with so we want to use the top most function block.
285             return &m_sc.function->GetBlock (false);
286         }
287     }
288     return NULL;
289 }
290 
291 //----------------------------------------------------------------------
292 // Get the symbol context if we already haven't done so by resolving the
293 // PC address as much as possible. This way when we pass around a
294 // StackFrame object, everyone will have as much information as
295 // possible and no one will ever have to look things up manually.
296 //----------------------------------------------------------------------
297 const SymbolContext&
298 StackFrame::GetSymbolContext (uint32_t resolve_scope)
299 {
300     // Copy our internal symbol context into "sc".
301     if ((m_flags.GetAllFlagBits() & resolve_scope) != resolve_scope)
302     {
303         // Resolve our PC to section offset if we haven't alreday done so
304         // and if we don't have a module. The resolved address section will
305         // contain the module to which it belongs
306         if (!m_sc.module_sp && m_flags.IsClear(RESOLVED_FRAME_CODE_ADDR))
307             GetFrameCodeAddress();
308 
309         // If this is not frame zero, then we need to subtract 1 from the PC
310         // value when doing address lookups since the PC will be on the
311         // instruction following the function call instruction...
312 
313         Address lookup_addr(GetFrameCodeAddress());
314         if (m_frame_index > 0 && lookup_addr.IsValid())
315         {
316             addr_t offset = lookup_addr.GetOffset();
317             if (offset > 0)
318                 lookup_addr.SetOffset(offset - 1);
319         }
320 
321 
322         uint32_t resolved = 0;
323         if (m_sc.module_sp)
324         {
325             // We have something in our stack frame symbol context, lets check
326             // if we haven't already tried to lookup one of those things. If we
327             // haven't then we will do the query.
328 
329             uint32_t actual_resolve_scope = 0;
330 
331             if (resolve_scope & eSymbolContextCompUnit)
332             {
333                 if (m_flags.IsClear (eSymbolContextCompUnit))
334                 {
335                     if (m_sc.comp_unit)
336                         resolved |= eSymbolContextCompUnit;
337                     else
338                         actual_resolve_scope |= eSymbolContextCompUnit;
339                 }
340             }
341 
342             if (resolve_scope & eSymbolContextFunction)
343             {
344                 if (m_flags.IsClear (eSymbolContextFunction))
345                 {
346                     if (m_sc.function)
347                         resolved |= eSymbolContextFunction;
348                     else
349                         actual_resolve_scope |= eSymbolContextFunction;
350                 }
351             }
352 
353             if (resolve_scope & eSymbolContextBlock)
354             {
355                 if (m_flags.IsClear (eSymbolContextBlock))
356                 {
357                     if (m_sc.block)
358                         resolved |= eSymbolContextBlock;
359                     else
360                         actual_resolve_scope |= eSymbolContextBlock;
361                 }
362             }
363 
364             if (resolve_scope & eSymbolContextSymbol)
365             {
366                 if (m_flags.IsClear (eSymbolContextSymbol))
367                 {
368                     if (m_sc.symbol)
369                         resolved |= eSymbolContextSymbol;
370                     else
371                         actual_resolve_scope |= eSymbolContextSymbol;
372                 }
373             }
374 
375             if (resolve_scope & eSymbolContextLineEntry)
376             {
377                 if (m_flags.IsClear (eSymbolContextLineEntry))
378                 {
379                     if (m_sc.line_entry.IsValid())
380                         resolved |= eSymbolContextLineEntry;
381                     else
382                         actual_resolve_scope |= eSymbolContextLineEntry;
383                 }
384             }
385 
386             if (actual_resolve_scope)
387             {
388                 // We might be resolving less information than what is already
389                 // in our current symbol context so resolve into a temporary
390                 // symbol context "sc" so we don't clear out data we have
391                 // already found in "m_sc"
392                 SymbolContext sc;
393                 // Set flags that indicate what we have tried to resolve
394                 resolved |= m_sc.module_sp->ResolveSymbolContextForAddress (lookup_addr, actual_resolve_scope, sc);
395                 // Only replace what we didn't already have as we may have
396                 // information for an inlined function scope that won't match
397                 // what a standard lookup by address would match
398                 if ((resolved & eSymbolContextCompUnit)  && m_sc.comp_unit == NULL)
399                     m_sc.comp_unit = sc.comp_unit;
400                 if ((resolved & eSymbolContextFunction)  && m_sc.function == NULL)
401                     m_sc.function = sc.function;
402                 if ((resolved & eSymbolContextBlock)     && m_sc.block == NULL)
403                     m_sc.block = sc.block;
404                 if ((resolved & eSymbolContextSymbol)    && m_sc.symbol == NULL)
405                     m_sc.symbol = sc.symbol;
406                 if ((resolved & eSymbolContextLineEntry) && !m_sc.line_entry.IsValid())
407                     m_sc.line_entry = sc.line_entry;
408 
409             }
410         }
411         else
412         {
413             // If we don't have a module, then we can't have the compile unit,
414             // function, block, line entry or symbol, so we can safely call
415             // ResolveSymbolContextForAddress with our symbol context member m_sc.
416             resolved |= m_thread.GetProcess().GetTarget().GetImages().ResolveSymbolContextForAddress (lookup_addr, resolve_scope, m_sc);
417         }
418 
419         // If the target was requested add that:
420         if (m_sc.target_sp.get() == NULL)
421         {
422             m_sc.target_sp = CalculateProcess()->GetTarget().GetSP();
423             if (m_sc.target_sp)
424                 resolved |= eSymbolContextTarget;
425         }
426 
427         // Update our internal flags so we remember what we have tried to locate so
428         // we don't have to keep trying when more calls to this function are made.
429         // We might have dug up more information that was requested (for example
430         // if we were asked to only get the block, we will have gotten the
431         // compile unit, and function) so set any additional bits that we resolved
432         m_flags.Set (resolve_scope | resolved);
433     }
434 
435     // Return the symbol context with everything that was possible to resolve
436     // resolved.
437     return m_sc;
438 }
439 
440 
441 VariableList *
442 StackFrame::GetVariableList (bool get_file_globals)
443 {
444     if (m_flags.IsClear(RESOLVED_VARIABLES))
445     {
446         m_flags.Set(RESOLVED_VARIABLES);
447 
448         Block *frame_block = GetFrameBlock();
449 
450         if (frame_block)
451         {
452             const bool get_child_variables = true;
453             const bool can_create = true;
454             m_variable_list_sp = frame_block->GetVariableList (get_child_variables, can_create);
455         }
456 
457         if (get_file_globals)
458         {
459             if (m_flags.IsClear (eSymbolContextCompUnit))
460                 GetSymbolContext (eSymbolContextCompUnit);
461 
462             if (m_sc.comp_unit)
463             {
464                 VariableListSP global_variable_list_sp (m_sc.comp_unit->GetVariableList(true));
465                 if (m_variable_list_sp)
466                     m_variable_list_sp->AddVariables (global_variable_list_sp.get());
467                 else
468                     m_variable_list_sp = global_variable_list_sp;
469             }
470         }
471     }
472     return m_variable_list_sp.get();
473 }
474 
475 
476 bool
477 StackFrame::GetFrameBaseValue (Scalar &frame_base, Error *error_ptr)
478 {
479     if (m_flags.IsClear(GOT_FRAME_BASE))
480     {
481         if (m_sc.function)
482         {
483             m_frame_base.Clear();
484             m_frame_base_error.Clear();
485 
486             m_flags.Set(GOT_FRAME_BASE);
487             ExecutionContext exe_ctx (&m_thread.GetProcess(), &m_thread, this);
488             Value expr_value;
489             addr_t loclist_base_addr = LLDB_INVALID_ADDRESS;
490             if (m_sc.function->GetFrameBaseExpression().IsLocationList())
491                 loclist_base_addr = m_sc.function->GetAddressRange().GetBaseAddress().GetLoadAddress (&m_thread.GetProcess().GetTarget());
492 
493             if (m_sc.function->GetFrameBaseExpression().Evaluate(&exe_ctx, NULL, loclist_base_addr, NULL, expr_value, &m_frame_base_error) == false)
494             {
495                 // We should really have an error if evaluate returns, but in case
496                 // we don't, lets set the error to something at least.
497                 if (m_frame_base_error.Success())
498                     m_frame_base_error.SetErrorString("Evaluation of the frame base expression failed.");
499             }
500             else
501             {
502                 m_frame_base = expr_value.ResolveValue(&exe_ctx, NULL);
503             }
504         }
505         else
506         {
507             m_frame_base_error.SetErrorString ("No function in symbol context.");
508         }
509     }
510 
511     if (m_frame_base_error.Success())
512         frame_base = m_frame_base;
513 
514     if (error_ptr)
515         *error_ptr = m_frame_base_error;
516     return m_frame_base_error.Success();
517 }
518 
519 RegisterContext *
520 StackFrame::GetRegisterContext ()
521 {
522     if (m_reg_context_sp.get() == NULL)
523         m_reg_context_sp.reset (m_thread.CreateRegisterContextForFrame (this));
524     return m_reg_context_sp.get();
525 }
526 
527 bool
528 StackFrame::HasDebugInformation ()
529 {
530     GetSymbolContext (eSymbolContextLineEntry);
531     return m_sc.line_entry.IsValid();
532 }
533 
534 
535 ValueObjectSP
536 StackFrame::GetValueObjectForFrameVariable (const VariableSP &variable_sp)
537 {
538     ValueObjectSP valobj_sp;
539     VariableList *var_list = GetVariableList (true);
540     if (var_list)
541     {
542         // Make sure the variable is a frame variable
543         const uint32_t var_idx = var_list->FindIndexForVariable (variable_sp.get());
544         const uint32_t num_variables = var_list->GetSize();
545         if (var_idx < num_variables)
546         {
547             valobj_sp = m_variable_list_value_objects.GetValueObjectAtIndex (var_idx);
548             if (valobj_sp.get() == NULL)
549             {
550                 if (m_variable_list_value_objects.GetSize() < num_variables)
551                     m_variable_list_value_objects.Resize(num_variables);
552                 valobj_sp.reset (new ValueObjectVariable (variable_sp));
553                 m_variable_list_value_objects.SetValueObjectAtIndex (var_idx, valobj_sp);
554             }
555         }
556     }
557     return valobj_sp;
558 }
559 
560 ValueObjectSP
561 StackFrame::TrackGlobalVariable (const VariableSP &variable_sp)
562 {
563     // Check to make sure we aren't already tracking this variable?
564     ValueObjectSP valobj_sp (GetValueObjectForFrameVariable (variable_sp));
565     if (!valobj_sp)
566     {
567         // We aren't already tracking this global
568         VariableList *var_list = GetVariableList (true);
569         // If this frame has no variables, create a new list
570         if (var_list == NULL)
571             m_variable_list_sp.reset (new VariableList());
572 
573         // Add the global/static variable to this frame
574         m_variable_list_sp->AddVariable (variable_sp);
575 
576         // Now make a value object for it so we can track its changes
577         valobj_sp = GetValueObjectForFrameVariable (variable_sp);
578     }
579     return valobj_sp;
580 }
581 
582 bool
583 StackFrame::IsInlined ()
584 {
585     if (m_sc.block == NULL)
586         GetSymbolContext (eSymbolContextBlock);
587     if (m_sc.block)
588         return m_sc.block->GetContainingInlinedBlock() != NULL;
589     return false;
590 }
591 
592 Target *
593 StackFrame::CalculateTarget ()
594 {
595     return m_thread.CalculateTarget();
596 }
597 
598 Process *
599 StackFrame::CalculateProcess ()
600 {
601     return m_thread.CalculateProcess();
602 }
603 
604 Thread *
605 StackFrame::CalculateThread ()
606 {
607     return &m_thread;
608 }
609 
610 StackFrame *
611 StackFrame::CalculateStackFrame ()
612 {
613     return this;
614 }
615 
616 
617 void
618 StackFrame::Calculate (ExecutionContext &exe_ctx)
619 {
620     m_thread.Calculate (exe_ctx);
621     exe_ctx.frame = this;
622 }
623 
624 void
625 StackFrame::Dump (Stream *strm, bool show_frame_index, bool show_fullpaths)
626 {
627     if (strm == NULL)
628         return;
629 
630     if (show_frame_index)
631         strm->Printf("frame #%u: ", m_frame_index);
632     strm->Printf("0x%0*llx ", m_thread.GetProcess().GetAddressByteSize() * 2, GetFrameCodeAddress().GetLoadAddress(&m_thread.GetProcess().GetTarget()));
633     GetSymbolContext(eSymbolContextEverything);
634     const bool show_module = true;
635     const bool show_inline = true;
636     m_sc.DumpStopContext(strm, &m_thread.GetProcess(), GetFrameCodeAddress(), show_fullpaths, show_module, show_inline);
637 }
638 
639 void
640 StackFrame::UpdateCurrentFrameFromPreviousFrame (StackFrame &prev_frame)
641 {
642     assert (GetStackID() == prev_frame.GetStackID());    // TODO: remove this after some testing
643     m_variable_list_sp = prev_frame.m_variable_list_sp;
644     m_variable_list_value_objects.Swap (prev_frame.m_variable_list_value_objects);
645     if (!m_disassembly.GetString().empty())
646         m_disassembly.GetString().swap (m_disassembly.GetString());
647 }
648 
649 
650 void
651 StackFrame::UpdatePreviousFrameFromCurrentFrame (StackFrame &curr_frame)
652 {
653     assert (GetStackID() == curr_frame.GetStackID());        // TODO: remove this after some testing
654     m_id.SetPC (curr_frame.m_id.GetPC());       // Update the Stack ID PC value
655     assert (&m_thread == &curr_frame.m_thread);
656     m_frame_index = curr_frame.m_frame_index;
657     m_unwind_frame_index = curr_frame.m_unwind_frame_index;
658     m_reg_context_sp = curr_frame.m_reg_context_sp;
659     m_frame_code_addr = curr_frame.m_frame_code_addr;
660     assert (m_sc.target_sp.get() == NULL || curr_frame.m_sc.target_sp.get() == NULL || m_sc.target_sp.get() == curr_frame.m_sc.target_sp.get());
661     assert (m_sc.module_sp.get() == NULL || curr_frame.m_sc.module_sp.get() == NULL || m_sc.module_sp.get() == curr_frame.m_sc.module_sp.get());
662     assert (m_sc.comp_unit == NULL || curr_frame.m_sc.comp_unit == NULL || m_sc.comp_unit == curr_frame.m_sc.comp_unit);
663     assert (m_sc.function == NULL || curr_frame.m_sc.function == NULL || m_sc.function == curr_frame.m_sc.function);
664     m_sc = curr_frame.m_sc;
665     m_flags.Clear(GOT_FRAME_BASE | eSymbolContextEverything);
666     m_flags.Set (m_sc.GetResolvedMask());
667     m_frame_base.Clear();
668     m_frame_base_error.Clear();
669 }
670 
671 
672 bool
673 StackFrame::HasCachedData () const
674 {
675     if (m_variable_list_sp.get())
676         return true;
677     if (m_variable_list_value_objects.GetSize() > 0)
678         return true;
679     if (!m_disassembly.GetString().empty())
680         return true;
681     return false;
682 }