1ac7ddfbfSEd Maste //===-- ExpressionSourceCode.cpp --------------------------------*- C++ -*-===//
2ac7ddfbfSEd Maste //
3ac7ddfbfSEd Maste // The LLVM Compiler Infrastructure
4ac7ddfbfSEd Maste //
5ac7ddfbfSEd Maste // This file is distributed under the University of Illinois Open Source
6ac7ddfbfSEd Maste // License. See LICENSE.TXT for details.
7ac7ddfbfSEd Maste //
8ac7ddfbfSEd Maste //===----------------------------------------------------------------------===//
9ac7ddfbfSEd Maste
10ac7ddfbfSEd Maste #include "lldb/Expression/ExpressionSourceCode.h"
11ac7ddfbfSEd Maste
129f2f44ceSEd Maste #include "Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.h"
139f2f44ceSEd Maste #include "Plugins/ExpressionParser/Clang/ClangPersistentVariables.h"
14435933ddSDimitry Andric #include "lldb/Symbol/Block.h"
159f2f44ceSEd Maste #include "lldb/Symbol/CompileUnit.h"
169f2f44ceSEd Maste #include "lldb/Symbol/DebugMacros.h"
179f2f44ceSEd Maste #include "lldb/Symbol/TypeSystem.h"
184bb0738eSEd Maste #include "lldb/Symbol/VariableList.h"
190127ef0fSEd Maste #include "lldb/Target/ExecutionContext.h"
204bb0738eSEd Maste #include "lldb/Target/Language.h"
210127ef0fSEd Maste #include "lldb/Target/Platform.h"
221c3bbb01SEd Maste #include "lldb/Target/StackFrame.h"
230127ef0fSEd Maste #include "lldb/Target/Target.h"
24f678e45dSDimitry Andric #include "lldb/Utility/StreamString.h"
25ac7ddfbfSEd Maste
26ac7ddfbfSEd Maste using namespace lldb_private;
27ac7ddfbfSEd Maste
28435933ddSDimitry Andric const char *ExpressionSourceCode::g_expression_prefix = R"(
291c3bbb01SEd Maste #ifndef NULL
30ac7ddfbfSEd Maste #define NULL (__null)
311c3bbb01SEd Maste #endif
321c3bbb01SEd Maste #ifndef Nil
33ac7ddfbfSEd Maste #define Nil (__null)
341c3bbb01SEd Maste #endif
351c3bbb01SEd Maste #ifndef nil
36ac7ddfbfSEd Maste #define nil (__null)
371c3bbb01SEd Maste #endif
381c3bbb01SEd Maste #ifndef YES
39ac7ddfbfSEd Maste #define YES ((BOOL)1)
401c3bbb01SEd Maste #endif
411c3bbb01SEd Maste #ifndef NO
42ac7ddfbfSEd Maste #define NO ((BOOL)0)
431c3bbb01SEd Maste #endif
440127ef0fSEd Maste typedef __INT8_TYPE__ int8_t;
450127ef0fSEd Maste typedef __UINT8_TYPE__ uint8_t;
460127ef0fSEd Maste typedef __INT16_TYPE__ int16_t;
470127ef0fSEd Maste typedef __UINT16_TYPE__ uint16_t;
480127ef0fSEd Maste typedef __INT32_TYPE__ int32_t;
490127ef0fSEd Maste typedef __UINT32_TYPE__ uint32_t;
500127ef0fSEd Maste typedef __INT64_TYPE__ int64_t;
510127ef0fSEd Maste typedef __UINT64_TYPE__ uint64_t;
520127ef0fSEd Maste typedef __INTPTR_TYPE__ intptr_t;
530127ef0fSEd Maste typedef __UINTPTR_TYPE__ uintptr_t;
54ac7ddfbfSEd Maste typedef __SIZE_TYPE__ size_t;
55ac7ddfbfSEd Maste typedef __PTRDIFF_TYPE__ ptrdiff_t;
56ac7ddfbfSEd Maste typedef unsigned short unichar;
571c3bbb01SEd Maste extern "C"
581c3bbb01SEd Maste {
591c3bbb01SEd Maste int printf(const char * __restrict, ...);
601c3bbb01SEd Maste }
61ac7ddfbfSEd Maste )";
62ac7ddfbfSEd Maste
634bb0738eSEd Maste static const char *c_start_marker = " /*LLDB_BODY_START*/\n ";
644bb0738eSEd Maste static const char *c_end_marker = ";\n /*LLDB_BODY_END*/\n";
654bb0738eSEd Maste
669f2f44ceSEd Maste namespace {
679f2f44ceSEd Maste
68435933ddSDimitry Andric class AddMacroState {
69435933ddSDimitry Andric enum State {
709f2f44ceSEd Maste CURRENT_FILE_NOT_YET_PUSHED,
719f2f44ceSEd Maste CURRENT_FILE_PUSHED,
729f2f44ceSEd Maste CURRENT_FILE_POPPED
739f2f44ceSEd Maste };
749f2f44ceSEd Maste
759f2f44ceSEd Maste public:
AddMacroState(const FileSpec & current_file,const uint32_t current_file_line)769f2f44ceSEd Maste AddMacroState(const FileSpec ¤t_file, const uint32_t current_file_line)
77435933ddSDimitry Andric : m_state(CURRENT_FILE_NOT_YET_PUSHED), m_current_file(current_file),
78435933ddSDimitry Andric m_current_file_line(current_file_line) {}
799f2f44ceSEd Maste
StartFile(const FileSpec & file)80435933ddSDimitry Andric void StartFile(const FileSpec &file) {
819f2f44ceSEd Maste m_file_stack.push_back(file);
829f2f44ceSEd Maste if (file == m_current_file)
839f2f44ceSEd Maste m_state = CURRENT_FILE_PUSHED;
849f2f44ceSEd Maste }
859f2f44ceSEd Maste
EndFile()86435933ddSDimitry Andric void EndFile() {
879f2f44ceSEd Maste if (m_file_stack.size() == 0)
889f2f44ceSEd Maste return;
899f2f44ceSEd Maste
909f2f44ceSEd Maste FileSpec old_top = m_file_stack.back();
919f2f44ceSEd Maste m_file_stack.pop_back();
929f2f44ceSEd Maste if (old_top == m_current_file)
939f2f44ceSEd Maste m_state = CURRENT_FILE_POPPED;
949f2f44ceSEd Maste }
959f2f44ceSEd Maste
964ba319b5SDimitry Andric // An entry is valid if it occurs before the current line in the current
974ba319b5SDimitry Andric // file.
IsValidEntry(uint32_t line)98435933ddSDimitry Andric bool IsValidEntry(uint32_t line) {
99435933ddSDimitry Andric switch (m_state) {
1009f2f44ceSEd Maste case CURRENT_FILE_NOT_YET_PUSHED:
1019f2f44ceSEd Maste return true;
1029f2f44ceSEd Maste case CURRENT_FILE_PUSHED:
1034ba319b5SDimitry Andric // If we are in file included in the current file, the entry should be
1044ba319b5SDimitry Andric // added.
1059f2f44ceSEd Maste if (m_file_stack.back() != m_current_file)
1069f2f44ceSEd Maste return true;
1079f2f44ceSEd Maste
108*b5893f02SDimitry Andric return line < m_current_file_line;
1094bb0738eSEd Maste default:
1104bb0738eSEd Maste return false;
1119f2f44ceSEd Maste }
1129f2f44ceSEd Maste }
1139f2f44ceSEd Maste
1149f2f44ceSEd Maste private:
1159f2f44ceSEd Maste std::vector<FileSpec> m_file_stack;
1169f2f44ceSEd Maste State m_state;
1179f2f44ceSEd Maste FileSpec m_current_file;
1189f2f44ceSEd Maste uint32_t m_current_file_line;
1199f2f44ceSEd Maste };
1209f2f44ceSEd Maste
1219f2f44ceSEd Maste } // anonymous namespace
1229f2f44ceSEd Maste
AddMacros(const DebugMacros * dm,CompileUnit * comp_unit,AddMacroState & state,StreamString & stream)123435933ddSDimitry Andric static void AddMacros(const DebugMacros *dm, CompileUnit *comp_unit,
124435933ddSDimitry Andric AddMacroState &state, StreamString &stream) {
1259f2f44ceSEd Maste if (dm == nullptr)
1269f2f44ceSEd Maste return;
1279f2f44ceSEd Maste
128435933ddSDimitry Andric for (size_t i = 0; i < dm->GetNumMacroEntries(); i++) {
1299f2f44ceSEd Maste const DebugMacroEntry &entry = dm->GetMacroEntryAtIndex(i);
1309f2f44ceSEd Maste uint32_t line;
1319f2f44ceSEd Maste
132435933ddSDimitry Andric switch (entry.GetType()) {
1339f2f44ceSEd Maste case DebugMacroEntry::DEFINE:
1349f2f44ceSEd Maste if (state.IsValidEntry(entry.GetLineNumber()))
1359f2f44ceSEd Maste stream.Printf("#define %s\n", entry.GetMacroString().AsCString());
1369f2f44ceSEd Maste else
1379f2f44ceSEd Maste return;
1389f2f44ceSEd Maste break;
1399f2f44ceSEd Maste case DebugMacroEntry::UNDEF:
1409f2f44ceSEd Maste if (state.IsValidEntry(entry.GetLineNumber()))
1419f2f44ceSEd Maste stream.Printf("#undef %s\n", entry.GetMacroString().AsCString());
1429f2f44ceSEd Maste else
1439f2f44ceSEd Maste return;
1449f2f44ceSEd Maste break;
1459f2f44ceSEd Maste case DebugMacroEntry::START_FILE:
1469f2f44ceSEd Maste line = entry.GetLineNumber();
1479f2f44ceSEd Maste if (state.IsValidEntry(line))
1489f2f44ceSEd Maste state.StartFile(entry.GetFileSpec(comp_unit));
1499f2f44ceSEd Maste else
1509f2f44ceSEd Maste return;
1519f2f44ceSEd Maste break;
1529f2f44ceSEd Maste case DebugMacroEntry::END_FILE:
1539f2f44ceSEd Maste state.EndFile();
1549f2f44ceSEd Maste break;
1559f2f44ceSEd Maste case DebugMacroEntry::INDIRECT:
1569f2f44ceSEd Maste AddMacros(entry.GetIndirectDebugMacros(), comp_unit, state, stream);
1579f2f44ceSEd Maste break;
1589f2f44ceSEd Maste default:
1599f2f44ceSEd Maste // This is an unknown/invalid entry. Ignore.
1609f2f44ceSEd Maste break;
1619f2f44ceSEd Maste }
1629f2f44ceSEd Maste }
1639f2f44ceSEd Maste }
164ac7ddfbfSEd Maste
AddLocalVariableDecls(const lldb::VariableListSP & var_list_sp,StreamString & stream)165435933ddSDimitry Andric static void AddLocalVariableDecls(const lldb::VariableListSP &var_list_sp,
166435933ddSDimitry Andric StreamString &stream) {
167435933ddSDimitry Andric for (size_t i = 0; i < var_list_sp->GetSize(); i++) {
1684bb0738eSEd Maste lldb::VariableSP var_sp = var_list_sp->GetVariableAtIndex(i);
1694bb0738eSEd Maste
1704bb0738eSEd Maste ConstString var_name = var_sp->GetName();
171435933ddSDimitry Andric if (!var_name || var_name == ConstString("this") ||
172435933ddSDimitry Andric var_name == ConstString(".block_descriptor"))
1734bb0738eSEd Maste continue;
1744bb0738eSEd Maste
1754bb0738eSEd Maste stream.Printf("using $__lldb_local_vars::%s;\n", var_name.AsCString());
1764bb0738eSEd Maste }
1774bb0738eSEd Maste }
1784bb0738eSEd Maste
GetText(std::string & text,lldb::LanguageType wrapping_language,bool static_method,ExecutionContext & exe_ctx) const179435933ddSDimitry Andric bool ExpressionSourceCode::GetText(std::string &text,
180435933ddSDimitry Andric lldb::LanguageType wrapping_language,
181435933ddSDimitry Andric bool static_method,
182435933ddSDimitry Andric ExecutionContext &exe_ctx) const {
1830127ef0fSEd Maste const char *target_specific_defines = "typedef signed char BOOL;\n";
1841c3bbb01SEd Maste std::string module_macros;
1850127ef0fSEd Maste
1864bb0738eSEd Maste Target *target = exe_ctx.GetTargetPtr();
187435933ddSDimitry Andric if (target) {
188435933ddSDimitry Andric if (target->GetArchitecture().GetMachine() == llvm::Triple::aarch64) {
1890127ef0fSEd Maste target_specific_defines = "typedef bool BOOL;\n";
1900127ef0fSEd Maste }
191435933ddSDimitry Andric if (target->GetArchitecture().GetMachine() == llvm::Triple::x86_64) {
192435933ddSDimitry Andric if (lldb::PlatformSP platform_sp = target->GetPlatform()) {
1931c3bbb01SEd Maste static ConstString g_platform_ios_simulator("ios-simulator");
194435933ddSDimitry Andric if (platform_sp->GetPluginName() == g_platform_ios_simulator) {
1950127ef0fSEd Maste target_specific_defines = "typedef bool BOOL;\n";
1960127ef0fSEd Maste }
1970127ef0fSEd Maste }
1980127ef0fSEd Maste }
1991c3bbb01SEd Maste
200435933ddSDimitry Andric if (ClangModulesDeclVendor *decl_vendor =
201435933ddSDimitry Andric target->GetClangModulesDeclVendor()) {
202435933ddSDimitry Andric ClangPersistentVariables *persistent_vars =
203435933ddSDimitry Andric llvm::cast<ClangPersistentVariables>(
204435933ddSDimitry Andric target->GetPersistentExpressionStateForLanguage(
205435933ddSDimitry Andric lldb::eLanguageTypeC));
206435933ddSDimitry Andric const ClangModulesDeclVendor::ModuleVector &hand_imported_modules =
207435933ddSDimitry Andric persistent_vars->GetHandLoadedClangModules();
2081c3bbb01SEd Maste ClangModulesDeclVendor::ModuleVector modules_for_macros;
2091c3bbb01SEd Maste
210435933ddSDimitry Andric for (ClangModulesDeclVendor::ModuleID module : hand_imported_modules) {
2111c3bbb01SEd Maste modules_for_macros.push_back(module);
2121c3bbb01SEd Maste }
2131c3bbb01SEd Maste
214435933ddSDimitry Andric if (target->GetEnableAutoImportClangModules()) {
215435933ddSDimitry Andric if (StackFrame *frame = exe_ctx.GetFramePtr()) {
216435933ddSDimitry Andric if (Block *block = frame->GetFrameBlock()) {
2171c3bbb01SEd Maste SymbolContext sc;
2181c3bbb01SEd Maste
2191c3bbb01SEd Maste block->CalculateSymbolContext(&sc);
2201c3bbb01SEd Maste
221435933ddSDimitry Andric if (sc.comp_unit) {
2221c3bbb01SEd Maste StreamString error_stream;
2231c3bbb01SEd Maste
224435933ddSDimitry Andric decl_vendor->AddModulesForCompileUnit(
225435933ddSDimitry Andric *sc.comp_unit, modules_for_macros, error_stream);
2261c3bbb01SEd Maste }
2271c3bbb01SEd Maste }
2281c3bbb01SEd Maste }
2291c3bbb01SEd Maste }
2301c3bbb01SEd Maste
231435933ddSDimitry Andric decl_vendor->ForEachMacro(
232435933ddSDimitry Andric modules_for_macros,
233435933ddSDimitry Andric [&module_macros](const std::string &expansion) -> bool {
2341c3bbb01SEd Maste module_macros.append(expansion);
2351c3bbb01SEd Maste module_macros.append("\n");
2361c3bbb01SEd Maste return false;
2371c3bbb01SEd Maste });
2381c3bbb01SEd Maste }
2390127ef0fSEd Maste }
2400127ef0fSEd Maste
2419f2f44ceSEd Maste StreamString debug_macros_stream;
2424bb0738eSEd Maste StreamString lldb_local_var_decls;
243435933ddSDimitry Andric if (StackFrame *frame = exe_ctx.GetFramePtr()) {
2449f2f44ceSEd Maste const SymbolContext &sc = frame->GetSymbolContext(
2459f2f44ceSEd Maste lldb::eSymbolContextCompUnit | lldb::eSymbolContextLineEntry);
2469f2f44ceSEd Maste
247435933ddSDimitry Andric if (sc.comp_unit && sc.line_entry.IsValid()) {
2489f2f44ceSEd Maste DebugMacros *dm = sc.comp_unit->GetDebugMacros();
249435933ddSDimitry Andric if (dm) {
2509f2f44ceSEd Maste AddMacroState state(sc.line_entry.file, sc.line_entry.line);
2519f2f44ceSEd Maste AddMacros(dm, sc.comp_unit, state, debug_macros_stream);
2529f2f44ceSEd Maste }
2539f2f44ceSEd Maste }
2544bb0738eSEd Maste
2554bb0738eSEd Maste ConstString object_name;
256435933ddSDimitry Andric if (Language::LanguageIsCPlusPlus(frame->GetLanguage())) {
257435933ddSDimitry Andric if (target->GetInjectLocalVariables(&exe_ctx)) {
258435933ddSDimitry Andric lldb::VariableListSP var_list_sp =
259435933ddSDimitry Andric frame->GetInScopeVariableList(false, true);
2604bb0738eSEd Maste AddLocalVariableDecls(var_list_sp, lldb_local_var_decls);
2614bb0738eSEd Maste }
2624bb0738eSEd Maste }
2639f2f44ceSEd Maste }
2649f2f44ceSEd Maste
265435933ddSDimitry Andric if (m_wrap) {
266435933ddSDimitry Andric switch (wrapping_language) {
267ac7ddfbfSEd Maste default:
268ac7ddfbfSEd Maste return false;
269ac7ddfbfSEd Maste case lldb::eLanguageTypeC:
270ac7ddfbfSEd Maste case lldb::eLanguageTypeC_plus_plus:
271ac7ddfbfSEd Maste case lldb::eLanguageTypeObjC:
272ac7ddfbfSEd Maste break;
273ac7ddfbfSEd Maste }
274ac7ddfbfSEd Maste
275ac7ddfbfSEd Maste StreamString wrap_stream;
276ac7ddfbfSEd Maste
277435933ddSDimitry Andric wrap_stream.Printf("%s\n%s\n%s\n%s\n%s\n", module_macros.c_str(),
278435933ddSDimitry Andric debug_macros_stream.GetData(), g_expression_prefix,
279435933ddSDimitry Andric target_specific_defines, m_prefix.c_str());
2801c3bbb01SEd Maste
281435933ddSDimitry Andric // First construct a tagged form of the user expression so we can find it
282435933ddSDimitry Andric // later:
2834bb0738eSEd Maste std::string tagged_body;
284435933ddSDimitry Andric switch (wrapping_language) {
2854bb0738eSEd Maste default:
2864bb0738eSEd Maste tagged_body = m_body;
2874bb0738eSEd Maste break;
2884bb0738eSEd Maste case lldb::eLanguageTypeC:
2894bb0738eSEd Maste case lldb::eLanguageTypeC_plus_plus:
2904bb0738eSEd Maste case lldb::eLanguageTypeObjC:
2914bb0738eSEd Maste tagged_body.append(c_start_marker);
2924bb0738eSEd Maste tagged_body.append(m_body);
2934bb0738eSEd Maste tagged_body.append(c_end_marker);
2944bb0738eSEd Maste break;
2954bb0738eSEd Maste }
296435933ddSDimitry Andric switch (wrapping_language) {
297ac7ddfbfSEd Maste default:
298ac7ddfbfSEd Maste break;
299ac7ddfbfSEd Maste case lldb::eLanguageTypeC:
3001c3bbb01SEd Maste wrap_stream.Printf("void \n"
301ac7ddfbfSEd Maste "%s(void *$__lldb_arg) \n"
302ac7ddfbfSEd Maste "{ \n"
303ac7ddfbfSEd Maste " %s; \n"
3044bb0738eSEd Maste "%s"
305ac7ddfbfSEd Maste "} \n",
306435933ddSDimitry Andric m_name.c_str(), lldb_local_var_decls.GetData(),
3074bb0738eSEd Maste tagged_body.c_str());
308ac7ddfbfSEd Maste break;
309ac7ddfbfSEd Maste case lldb::eLanguageTypeC_plus_plus:
3101c3bbb01SEd Maste wrap_stream.Printf("void \n"
3114bb0738eSEd Maste "$__lldb_class::%s(void *$__lldb_arg) \n"
312ac7ddfbfSEd Maste "{ \n"
313ac7ddfbfSEd Maste " %s; \n"
3144bb0738eSEd Maste "%s"
315ac7ddfbfSEd Maste "} \n",
316435933ddSDimitry Andric m_name.c_str(), lldb_local_var_decls.GetData(),
3174bb0738eSEd Maste tagged_body.c_str());
318ac7ddfbfSEd Maste break;
319ac7ddfbfSEd Maste case lldb::eLanguageTypeObjC:
320435933ddSDimitry Andric if (static_method) {
321435933ddSDimitry Andric wrap_stream.Printf(
322435933ddSDimitry Andric "@interface $__lldb_objc_class ($__lldb_category) \n"
323ac7ddfbfSEd Maste "+(void)%s:(void *)$__lldb_arg; \n"
324ac7ddfbfSEd Maste "@end \n"
325ac7ddfbfSEd Maste "@implementation $__lldb_objc_class ($__lldb_category) \n"
326ac7ddfbfSEd Maste "+(void)%s:(void *)$__lldb_arg \n"
327ac7ddfbfSEd Maste "{ \n"
3284bb0738eSEd Maste "%s"
329ac7ddfbfSEd Maste "} \n"
330ac7ddfbfSEd Maste "@end \n",
331435933ddSDimitry Andric m_name.c_str(), m_name.c_str(), tagged_body.c_str());
332435933ddSDimitry Andric } else {
333435933ddSDimitry Andric wrap_stream.Printf(
334435933ddSDimitry Andric "@interface $__lldb_objc_class ($__lldb_category) \n"
335ac7ddfbfSEd Maste "-(void)%s:(void *)$__lldb_arg; \n"
336ac7ddfbfSEd Maste "@end \n"
337ac7ddfbfSEd Maste "@implementation $__lldb_objc_class ($__lldb_category) \n"
338ac7ddfbfSEd Maste "-(void)%s:(void *)$__lldb_arg \n"
339ac7ddfbfSEd Maste "{ \n"
3404bb0738eSEd Maste "%s"
341ac7ddfbfSEd Maste "} \n"
342ac7ddfbfSEd Maste "@end \n",
343435933ddSDimitry Andric m_name.c_str(), m_name.c_str(), tagged_body.c_str());
344ac7ddfbfSEd Maste }
345ac7ddfbfSEd Maste break;
346ac7ddfbfSEd Maste }
347ac7ddfbfSEd Maste
348ac7ddfbfSEd Maste text = wrap_stream.GetString();
349435933ddSDimitry Andric } else {
350ac7ddfbfSEd Maste text.append(m_body);
351ac7ddfbfSEd Maste }
352ac7ddfbfSEd Maste
353ac7ddfbfSEd Maste return true;
354ac7ddfbfSEd Maste }
3554bb0738eSEd Maste
GetOriginalBodyBounds(std::string transformed_text,lldb::LanguageType wrapping_language,size_t & start_loc,size_t & end_loc)356435933ddSDimitry Andric bool ExpressionSourceCode::GetOriginalBodyBounds(
357435933ddSDimitry Andric std::string transformed_text, lldb::LanguageType wrapping_language,
358435933ddSDimitry Andric size_t &start_loc, size_t &end_loc) {
3594bb0738eSEd Maste const char *start_marker;
3604bb0738eSEd Maste const char *end_marker;
3614bb0738eSEd Maste
362435933ddSDimitry Andric switch (wrapping_language) {
3634bb0738eSEd Maste default:
3644bb0738eSEd Maste return false;
3654bb0738eSEd Maste case lldb::eLanguageTypeC:
3664bb0738eSEd Maste case lldb::eLanguageTypeC_plus_plus:
3674bb0738eSEd Maste case lldb::eLanguageTypeObjC:
3684bb0738eSEd Maste start_marker = c_start_marker;
3694bb0738eSEd Maste end_marker = c_end_marker;
3704bb0738eSEd Maste break;
3714bb0738eSEd Maste }
3724bb0738eSEd Maste
3734bb0738eSEd Maste start_loc = transformed_text.find(start_marker);
3744bb0738eSEd Maste if (start_loc == std::string::npos)
3754bb0738eSEd Maste return false;
3764bb0738eSEd Maste start_loc += strlen(start_marker);
3774bb0738eSEd Maste end_loc = transformed_text.find(end_marker);
378*b5893f02SDimitry Andric return end_loc != std::string::npos;
3794bb0738eSEd Maste }
380