diff --git a/sourcepawn/compiler/memory-buffer.h b/sourcepawn/compiler/memory-buffer.h index 3800f921..0a92c285 100644 --- a/sourcepawn/compiler/memory-buffer.h +++ b/sourcepawn/compiler/memory-buffer.h @@ -22,7 +22,16 @@ #include #include "smx-builder.h" -class MemoryBuffer : public ke::ISmxBuffer +// Interface for SmxBuilder to blit bytes. +class ISmxBuffer +{ + public: + virtual bool write(const void *bytes, size_t len) = 0; + virtual size_t pos() const = 0; +}; + +// An in-memory buffer for SmxBuilder. +class MemoryBuffer : public ISmxBuffer { static const size_t kDefaultSize = 4096; diff --git a/sourcepawn/compiler/sc6.cpp b/sourcepawn/compiler/sc6.cpp index 88acfe30..0c2d6554 100644 --- a/sourcepawn/compiler/sc6.cpp +++ b/sourcepawn/compiler/sc6.cpp @@ -45,7 +45,7 @@ using namespace sp; using namespace ke; -static void append_dbginfo(void *fout); +static void append_debug_Tables(SmxBuilder *builder); typedef cell (*OPCODE_PROC)(Vector *buffer, char *params, cell opcode); @@ -615,10 +615,173 @@ static int sort_by_addr(const void *a1, const void *a2) return s1->addr - s2->addr; } +// Helper for parsing a debug string. Debug strings look like this: +// L:40 10 +class DebugString +{ + public: + DebugString() : kind_('\0'), str_(nullptr) + { } + DebugString(char *str) + : kind_(str[0]), + str_(str) + { + assert(str_[1] == ':'); + str_ += 2; + } + char kind() const { + return kind_; + } + ucell parse() { + return hex2long(str_, &str_); + } + char *skipspaces() { + str_ = ::skipwhitespace(str_); + return str_; + } + void expect(char c) { + assert(*str_ == c); + str_++; + } + char *skipto(char c) { + str_ = strchr(str_, c); + return str_; + } + char getc() { + return *str_++; + } + + private: + char kind_; + char *str_; +}; + +typedef SmxBlobSection SmxDebugInfoSection; +typedef SmxListSection SmxDebugLineSection; +typedef SmxListSection SmxDebugFileSection; +typedef SmxListSection SmxTagSection; +typedef SmxBlobSection SmxDebugSymbolsSection; + +static void append_debug_tables(SmxBuilder *builder, StringPool &pool) +{ + // We use a separate name table for historical reasons that are no longer + // necessary. In the future we should just alias this to ".names". + Ref names = new SmxNameTable(".dbg.strings"); + Ref info = new SmxDebugInfoSection(".dbg.info"); + Ref lines = new SmxDebugLineSection(".dbg.lines"); + Ref files = new SmxDebugFileSection(".dbg.files"); + Ref symbols = new SmxDebugSymbolsSection(".dbg.symbols"); + Ref tags = new SmxTagSection(".tags"); + + stringlist *dbgstrs = get_dbgstrings(); + + // State for tracking which file we're on. We replicate the original AMXDBG + // behavior here which excludes duplicate addresses. + ucell prev_file_addr = 0; + const char *prev_file_name = nullptr; + + // Add debug data. + for (stringlist *iter = dbgstrs; iter; iter = iter->next) { + if (iter->line[0] == '\0') + continue; + + DebugString str(iter->line); + switch (str.kind()) { + case 'F': + { + ucell codeidx = str.parse(); + if (codeidx != prev_file_addr) { + if (prev_file_name) { + sp_fdbg_file_t &entry = files->add(); + entry.addr = prev_file_addr; + entry.name = names->add(pool, prev_file_name); + } + prev_file_addr = codeidx; + } + prev_file_name = str.skipspaces(); + break; + } + + case 'L': + { + sp_fdbg_line_t &entry = lines->add(); + entry.addr = str.parse(); + entry.line = str.parse(); + break; + } + + case 'S': + { + sp_fdbg_symbol_t sym; + sp_fdbg_arraydim_t dims[sDIMEN_MAX]; + + sym.addr = str.parse(); + sym.tagid = str.parse(); + + str.skipspaces(); + str.expect(':'); + char *name = str.skipspaces(); + char *nameend = str.skipto(' '); + Atom *atom = pool.add(name, nameend - name); + + sym.codestart = str.parse(); + sym.codeend = str.parse(); + sym.ident = (char)str.parse(); + sym.vclass = (char)str.parse(); + sym.dimcount = 0; + sym.name = names->add(atom); + + info->header().num_syms++; + + str.skipspaces(); + if (str.getc() == '[') { + info->header().num_arrays++; + for (char *ptr = str.skipspaces(); *ptr != ']'; ptr = str.skipspaces()) { + dims[sym.dimcount].tagid = str.parse(); + str.skipspaces(); + str.expect(':'); + dims[sym.dimcount].size = str.parse(); + sym.dimcount++; + } + } + + symbols->add(&sym, sizeof(sym)); + symbols->add(dims, sizeof(dims[0]) * sym.dimcount); + break; + } + } + } + + // Add the last file. + if (prev_file_name) { + sp_fdbg_file_t &entry = files->add(); + entry.addr = prev_file_addr; + entry.name = names->add(pool, prev_file_name); + } + + // Build the tags table. + for (constvalue *constptr = tagname_tab.next; constptr; constptr = constptr->next) { + assert(strlen(constptr->name)>0); + + sp_file_tag_t &tag = tags->add(); + tag.tag_id = constptr->value; + tag.name = names->add(pool, constptr->name); + } + + info->header().num_files = files->count(); + info->header().num_lines = lines->count(); + + builder->add(files); + builder->add(symbols); + builder->add(lines); + builder->add(names); + builder->add(info); + builder->add(tags); +} + typedef SmxListSection SmxNativeSection; typedef SmxListSection SmxPublicSection; typedef SmxListSection SmxPubvarSection; -typedef SmxListSection SmxTagSection; typedef SmxBlobSection SmxDataSection; typedef SmxBlobSection SmxCodeSection; @@ -629,7 +792,6 @@ static void assemble_to_buffer(MemoryBuffer *buffer, void *fin) Ref natives = new SmxNativeSection(".natives"); Ref publics = new SmxPublicSection(".publics"); Ref pubvars = new SmxPubvarSection(".pubvars"); - Ref tags = new SmxTagSection(".tags"); Ref data = new SmxDataSection(".data"); Ref code = new SmxCodeSection(".code"); Ref names = new SmxNameTable(".names"); @@ -656,15 +818,6 @@ static void assemble_to_buffer(MemoryBuffer *buffer, void *fin) } } - // Build the tags table. - for (constvalue *constptr = tagname_tab.next; constptr; constptr = constptr->next) { - assert(strlen(constptr->name)>0); - - sp_file_tag_t &tag = tags->add(); - tag.tag_id = constptr->value; - tag.name = names->add(pool, constptr->name); - } - // Shuffle natives to be in address order. qsort(nativeList.buffer(), nativeList.length(), sizeof(symbol *), sort_by_addr); for (size_t i = 0; i < nativeList.length(); i++) { @@ -719,7 +872,8 @@ static void assemble_to_buffer(MemoryBuffer *buffer, void *fin) builder.add(pubvars); builder.add(natives); builder.add(names); - builder.add(tags); + append_debug_tables(&builder, pool); + builder.write(buffer); } @@ -775,225 +929,3 @@ void assemble(const char *binfname, void *fin) splat_to_binary(binfname, buffer.bytes(), buffer.size()); } - -int pc_writebin(void *handle,void *buffer,int size) { - assert(false); - return 1; -} - -static void append_dbginfo(void *fout) -{ - AMX_DBG_HDR dbghdr; - AMX_DBG_LINE dbgline; - AMX_DBG_SYMBOL dbgsym; - AMX_DBG_SYMDIM dbgidxtag[sDIMEN_MAX]; - int dim,dbgsymdim; - char *str,*prevstr,*name,*prevname; - ucell codeidx,previdx; - constvalue *constptr; - char symname[2*sNAMEMAX+16]; - int16_t id1,id2; - ucell address; - stringlist *dbgstrs = get_dbgstrings(); - stringlist *iter; - - /* header with general information */ - memset(&dbghdr, 0, sizeof dbghdr); - dbghdr.size=sizeof dbghdr; - dbghdr.magic=AMX_DBG_MAGIC; - dbghdr.file_version=CUR_FILE_VERSION; - dbghdr.amx_version=MIN_AMX_VERSION; - - dbgstrs=dbgstrs->next; - - /* first pass: collect the number of items in various tables */ - - /* file table */ - previdx=0; - prevstr=NULL; - prevname=NULL; - for (iter=dbgstrs; iter!=NULL; iter=iter->next) { - str = iter->line; - assert(str!=NULL); - assert(str[0]!='\0' && str[1]==':'); - if (str[0]=='F') { - codeidx=hex2long(str+2,&name); - if (codeidx!=previdx) { - if (prevstr!=NULL) { - assert(prevname!=NULL); - dbghdr.files++; - dbghdr.size+=sizeof(cell)+strlen(prevname)+1; - } /* if */ - previdx=codeidx; - } /* if */ - prevstr=str; - prevname=skipwhitespace(name); - } /* if */ - } /* for */ - if (prevstr!=NULL) { - assert(prevname!=NULL); - dbghdr.files++; - dbghdr.size+=sizeof(cell)+strlen(prevname)+1; - } /* if */ - - /* line number table */ - for (iter=dbgstrs; iter!=NULL; iter=iter->next) { - str = iter->line; - assert(str!=NULL); - assert(str[0]!='\0' && str[1]==':'); - if (str[0]=='L') { - dbghdr.lines++; - dbghdr.size+=sizeof(AMX_DBG_LINE); - } /* if */ - } /* for */ - - /* symbol table */ - for (iter=dbgstrs; iter!=NULL; iter=iter->next) { - str = iter->line; - assert(str!=NULL); - assert(str[0]!='\0' && str[1]==':'); - if (str[0]=='S') { - dbghdr.symbols++; - name=strchr(str+2,':'); - assert(name!=NULL); - dbghdr.size+=sizeof(AMX_DBG_SYMBOL)+strlen(skipwhitespace(name+1)); - if ((prevstr=strchr(name,'['))!=NULL) - while ((prevstr=strchr(prevstr+1,':'))!=NULL) - dbghdr.size+=sizeof(AMX_DBG_SYMDIM); - } /* if */ - } /* for */ - - /* tag table */ - for (constptr=tagname_tab.next; constptr!=NULL; constptr=constptr->next) { - assert(strlen(constptr->name)>0); - dbghdr.tags++; - dbghdr.size+=sizeof(AMX_DBG_TAG)+strlen(constptr->name); - } /* for */ - - /* automaton table */ - for (constptr=sc_automaton_tab.next; constptr!=NULL; constptr=constptr->next) { - assert((constptr->index==0 && strlen(constptr->name)==0) || strlen(constptr->name)>0); - dbghdr.automatons++; - dbghdr.size+=sizeof(AMX_DBG_MACHINE)+strlen(constptr->name); - } /* for */ - - /* state table */ - for (constptr=sc_state_tab.next; constptr!=NULL; constptr=constptr->next) { - assert(strlen(constptr->name)>0); - dbghdr.states++; - dbghdr.size+=sizeof(AMX_DBG_STATE)+strlen(constptr->name); - } /* for */ - - - /* pass 2: generate the tables */ - writeerror |= !pc_writebin(fout,&dbghdr,sizeof dbghdr); - - /* file table */ - previdx=0; - prevstr=NULL; - prevname=NULL; - for (iter=dbgstrs; iter!=NULL; iter=iter->next) { - str = iter->line; - assert(str[0]!='\0' && str[1]==':'); - if (str[0]=='F') { - codeidx=hex2long(str+2,&name); - if (codeidx!=previdx) { - if (prevstr!=NULL) { - assert(prevname!=NULL); - writeerror |= !pc_writebin(fout,&previdx,sizeof previdx); - writeerror |= !pc_writebin(fout,prevname,strlen(prevname)+1); - } /* if */ - previdx=codeidx; - } /* if */ - prevstr=str; - prevname=skipwhitespace(name); - } /* if */ - } /* for */ - if (prevstr!=NULL) { - assert(prevname!=NULL); - writeerror |= !pc_writebin(fout,&previdx,sizeof previdx); - writeerror |= !pc_writebin(fout,prevname,strlen(prevname)+1); - } /* if */ - - /* line number table */ - for (iter=dbgstrs; iter!=NULL; iter=iter->next) { - str = iter->line; - assert(str!=NULL); - assert(str[0]!='\0' && str[1]==':'); - if (str[0]=='L') { - dbgline.address=hex2long(str+2,&str); - dbgline.line=(int32_t)hex2long(str,NULL); - writeerror |= !pc_writebin(fout,&dbgline,sizeof dbgline); - } /* if */ - } /* for */ - - /* symbol table */ - for (iter=dbgstrs; iter!=NULL; iter=iter->next) { - str = iter->line; - assert(str!=NULL); - assert(str[0]!='\0' && str[1]==':'); - if (str[0]=='S') { - dbgsym.address=hex2long(str+2,&str); - dbgsym.tag=(int16_t)hex2long(str,&str); - str=skipwhitespace(str); - assert(*str==':'); - name=skipwhitespace(str+1); - str=strchr(name,' '); - assert(str!=NULL); - assert((int)(str-name)next) { - assert(strlen(constptr->name)>0); - id1=(int16_t)(constptr->value & TAGMASK); - writeerror |= !pc_writebin(fout,&id1,sizeof id1); - writeerror |= !pc_writebin(fout,constptr->name,strlen(constptr->name)+1); - } /* for */ - - /* automaton table */ - for (constptr=sc_automaton_tab.next; constptr!=NULL; constptr=constptr->next) { - assert((constptr->index==0 && strlen(constptr->name)==0) || strlen(constptr->name)>0); - id1=(int16_t)constptr->index; - address=(ucell)constptr->value; - writeerror |= !pc_writebin(fout,&id1,sizeof id1); - writeerror |= !pc_writebin(fout,&address,sizeof address); - writeerror |= !pc_writebin(fout,constptr->name,strlen(constptr->name)+1); - } /* for */ - - /* state table */ - for (constptr=sc_state_tab.next; constptr!=NULL; constptr=constptr->next) { - assert(strlen(constptr->name)>0); - id1=(int16_t)constptr->value; - id2=(int16_t)constptr->index; - address=(ucell)constptr->value; - writeerror |= !pc_writebin(fout,&id1,sizeof id1); - writeerror |= !pc_writebin(fout,&id2,sizeof id2); - writeerror |= !pc_writebin(fout,constptr->name,strlen(constptr->name)+1); - } /* for */ - - delete_dbgstringtable(); -} diff --git a/sourcepawn/compiler/smx-builder.h b/sourcepawn/compiler/smx-builder.h index 1b165c52..57b1eb6e 100644 --- a/sourcepawn/compiler/smx-builder.h +++ b/sourcepawn/compiler/smx-builder.h @@ -24,16 +24,11 @@ #include #include #include "string-pool.h" +#include "memory-buffer.h" namespace ke { -class ISmxBuffer -{ - public: - virtual bool write(const void *bytes, size_t len) = 0; - virtual size_t pos() const = 0; -}; - +// An SmxSection is a named blob of data. class SmxSection : public Refcounted { public: @@ -53,13 +48,19 @@ class SmxSection : public Refcounted AString name_; }; +// An SmxBlobSection is a section that has some kind of header structure +// (specified as a template parameter), and then an arbitrary run of bytes +// immediately after. template class SmxBlobSection : public SmxSection { public: SmxBlobSection(const char *name) - : SmxSection(name) + : SmxSection(name), + extra_(nullptr), + extra_len_(0) { + memset(&t_, 0, sizeof(t_)); } T &header() { @@ -72,6 +73,8 @@ class SmxBlobSection : public SmxSection bool write(ISmxBuffer *buf) KE_OVERRIDE { if (!buf->write(&t_, sizeof(t_))) return false; + if (!extra_len_) + return true; return buf->write(extra_, extra_len_); } size_t length() const KE_OVERRIDE { @@ -84,6 +87,32 @@ class SmxBlobSection : public SmxSection size_t extra_len_; }; +// An SmxBlobSection without headers. +template <> +class SmxBlobSection : public SmxSection +{ + public: + SmxBlobSection(const char *name) + : SmxSection(name) + { + } + + void add(void *bytes, size_t len) { + buffer_.write(bytes, len); + } + bool write(ISmxBuffer *buf) KE_OVERRIDE { + return buf->write(buffer_.bytes(), buffer_.size()); + } + size_t length() const KE_OVERRIDE { + return buffer_.size(); + } + + private: + MemoryBuffer buffer_; +}; + +// An SmxListSection is a section that is a simple table of uniformly-sized +// structures. It has no header of its own. template class SmxListSection : public SmxSection { @@ -104,18 +133,23 @@ class SmxListSection : public SmxSection return buf->write(list_.buffer(), list_.length() * sizeof(T)); } size_t length() const KE_OVERRIDE { - return list_.length() * sizeof(T); + return count() * sizeof(T); + } + size_t count() const { + return list_.length(); } private: Vector list_; }; +// A name table is a blob of zero-terminated strings. Strings are entered +// into the table as atoms, so duplicate stings are not emitted. class SmxNameTable : public SmxSection { public: SmxNameTable(const char *name) - : SmxSection(".names"), + : SmxSection(name), buffer_size_(0) { name_table_.init(64); diff --git a/sourcepawn/compiler/string-pool.h b/sourcepawn/compiler/string-pool.h index 94b21655..63ef10e0 100644 --- a/sourcepawn/compiler/string-pool.h +++ b/sourcepawn/compiler/string-pool.h @@ -19,14 +19,19 @@ #ifndef _include_jitcraft_string_pool_h_ #define _include_jitcraft_string_pool_h_ +// string-pool is a collection of helpers to atomize/internalize strings. The +// StringPool class provides Atom objects which can be used for efficiently +// handling string sets or dictionaries. + #include #include #include namespace ke { -// Wrapper around AString, since we might want to remove charfs() in a GC-safe -// implementation. +// An Atom is a string that has a unique instance. That is, any Atom("a") is +// guaranteed to be pointer-equivalent to another Atom("a"), as long as they +// originated from the same StringPool. class Atom { friend class StringPool; @@ -49,6 +54,7 @@ class Atom AString str_; }; +// Helper class to use as a key for hash table lookups. class CharsAndLength { public: @@ -76,6 +82,7 @@ class CharsAndLength size_t length_; }; +// Atom dictionary. class StringPool { public: