diff --git a/public/amtl/am-utility.h b/public/amtl/am-utility.h index 474c0052..41f69dc7 100644 --- a/public/amtl/am-utility.h +++ b/public/amtl/am-utility.h @@ -339,6 +339,25 @@ class StorageBuffer }; }; +template +class SaveAndSet +{ + public: + SaveAndSet(T *location, const T &value) + : location_(location), + old_(*location) + { + *location_ = value; + } + ~SaveAndSet() { + *location_ = old_; + } + + private: + T *location_; + T old_; +}; + #if defined(_MSC_VER) # define KE_SIZET_FMT "%Iu" #elif defined(__GNUC__) diff --git a/sourcepawn/compiler/AMBuilder b/sourcepawn/compiler/AMBuilder index 4d13ec91..b716d6bd 100644 --- a/sourcepawn/compiler/AMBuilder +++ b/sourcepawn/compiler/AMBuilder @@ -45,6 +45,7 @@ compiler.includes += [ os.path.join(builder.sourcePath, 'public', 'amtl'), os.path.join(builder.sourcePath, 'public', 'sourcepawn'), os.path.join(builder.sourcePath, 'sourcepawn', 'compiler'), + os.path.join(builder.sourcePath, 'sourcepawn', 'include'), os.path.join(builder.buildPath, 'includes'), os.path.join(builder.buildPath, builder.buildFolder), ] @@ -54,7 +55,8 @@ if compiler.cc.behavior == 'gcc': compiler.cflags += ['-Wno-format'] compiler.c_only_flags += ['-std=c99'] if builder.target_platform == 'linux': - compiler.postlink += ['-lgcc', '-lm'] + compiler.postlink += ['-lm'] + compiler.postlink += ['-lstdc++'] elif compiler.cc.behavior == 'msvc': compiler.linkflags.remove('/SUBSYSTEM:WINDOWS') compiler.linkflags.append('/SUBSYSTEM:CONSOLE') @@ -95,7 +97,7 @@ binary.sources += [ 'scstate.cpp', 'sctracker.cpp', 'scvars.cpp', - 'sp_file.cpp', + 'smx-builder.cpp', 'zlib/adler32.c', 'zlib/compress.c', 'zlib/crc32.c', diff --git a/sourcepawn/compiler/libpawnc.cpp b/sourcepawn/compiler/libpawnc.cpp index f99086d9..76a8cca2 100644 --- a/sourcepawn/compiler/libpawnc.cpp +++ b/sourcepawn/compiler/libpawnc.cpp @@ -1,4 +1,4 @@ -// vim: set sts=8 ts=4 sw=4 tw=99 noet: +// vim: set sts=8 ts=2 sw=2 tw=99 noet: /* LIBPAWNC.C * * A "glue file" for building the Pawn compiler as a DLL or shared library. @@ -341,42 +341,3 @@ char *pc_readasm(void *handle, char *string, int maxchars) { return mfgets((MEMFILE*)handle,string,maxchars); } - -extern memfile_t *bin_file; - -/* Should return a pointer, which is used as a "magic cookie" to all I/O - * functions; return NULL for failure. - */ -void *pc_openbin(char *filename) -{ - return memfile_creat(filename, 1); -} - -void pc_closebin(void *handle,int deletefile) -{ - if (deletefile) { - memfile_destroy((memfile_t *)handle); - bin_file = NULL; - } else { - bin_file = (memfile_t *)handle; - } -} - -/* pc_resetbin() - * Can seek to any location in the file. - * The offset is always from the start of the file. - */ -void pc_resetbin(void *handle,long offset) -{ - memfile_seek((memfile_t *)handle, offset); -} - -int pc_writebin(void *handle,void *buffer,int size) -{ - return memfile_write((memfile_t *)handle, buffer, size); -} - -long pc_lengthbin(void *handle) -{ - return memfile_tell((memfile_t *)handle); -} diff --git a/sourcepawn/compiler/memory-buffer.h b/sourcepawn/compiler/memory-buffer.h new file mode 100644 index 00000000..3800f921 --- /dev/null +++ b/sourcepawn/compiler/memory-buffer.h @@ -0,0 +1,94 @@ +// vim: set sts=2 ts=8 sw=2 tw=99 et: +// +// Copyright (C) 2012-2014 AlliedModders LLC, David Anderson +// +// This file is part of SourcePawn. +// +// SourcePawn is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, either version 3 of the License, or (at your option) +// any later version. +// +// SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along with +// SourcePawn. If not, see http://www.gnu.org/licenses/. +#ifndef _include_sp_memory_buffer_h_ +#define _include_sp_memory_buffer_h_ + +#include +#include +#include "smx-builder.h" + +class MemoryBuffer : public ke::ISmxBuffer +{ + static const size_t kDefaultSize = 4096; + + public: + MemoryBuffer() { + buffer_ = (uint8_t *)calloc(kDefaultSize, 1); + pos_ = buffer_; + end_ = buffer_ + kDefaultSize; + } + ~MemoryBuffer() { + free(buffer_); + } + + bool write(const void *bytes, size_t len) KE_OVERRIDE { + if (pos_ + len > end_) + grow(len); + memcpy(pos_, bytes, len); + pos_ += len; + return true; + } + + size_t pos() const KE_OVERRIDE { + return pos_ - buffer_; + } + + uint8_t *bytes() const { + return buffer_; + } + size_t size() const { + return pos(); + } + void rewind(size_t newpos) { + assert(newpos < pos()); + pos_ = buffer_ + newpos; + } + + private: + void grow(size_t len) { + if (!ke::IsUintPtrAddSafe(pos(), len)) { + fprintf(stderr, "Allocation overflow!\n"); + abort(); + } + + size_t new_maxsize = end_ - buffer_; + while (pos() + len > new_maxsize) { + if (!ke::IsUintPtrMultiplySafe(new_maxsize, 2)) { + fprintf(stderr, "Allocation overflow!\n"); + abort(); + } + new_maxsize *= 2; + } + + uint8_t *newbuffer = (uint8_t *)realloc(buffer_, new_maxsize); + if (!newbuffer) { + fprintf(stderr, "Out of memory!\n"); + abort(); + } + pos_ = newbuffer + (pos_ - buffer_); + end_ = newbuffer + new_maxsize; + buffer_ = newbuffer; + } + + private: + uint8_t *buffer_; + uint8_t *pos_; + uint8_t *end_; +}; + +#endif // _include_sp_memory_buffer_h_ diff --git a/sourcepawn/compiler/pawncc.cpp b/sourcepawn/compiler/pawncc.cpp index 10ceaa96..d98bcd75 100644 --- a/sourcepawn/compiler/pawncc.cpp +++ b/sourcepawn/compiler/pawncc.cpp @@ -3,557 +3,19 @@ #include #include #include "memfile.h" -#include "sp_file.h" -#include "amx.h" -#include "amxdbg.h" #include "osdefs.h" -#include "zlib/zlib.h" #if defined LINUX || defined DARWIN #include #elif defined WIN32 #include #endif - -enum FileSections -{ - FS_Code, /* required */ - FS_Data, /* required */ - FS_Publics, - FS_Pubvars, - FS_Natives, - FS_Nametable, /* required */ - FS_DbgFile, - FS_DbgSymbol, - FS_DbgLine, - FS_DbgTags, - FS_DbgNatives, - FS_DbgAutomaton, - FS_DbgState, - FS_DbgStrings, - FS_DbgInfo, - FS_Tags, - /* --- */ - FS_Number, -}; - -int pc_printf(const char *message,...); -int pc_compile(int argc, char **argv); -void sfwrite(const void *buf, size_t size, size_t count, sp_file_t *spf); - -memfile_t *bin_file = NULL; -jmp_buf brkout; - -#define sARGS_MAX 32 /* number of arguments a function can have, max */ -#define sDIMEN_MAX 4 /* maximum number of array dimensions */ - -typedef struct t_arg_s -{ - uint8_t ident; - int16_t tag; - char *name; - uint16_t dimcount; - sp_fdbg_arraydim_t dims[sDIMEN_MAX]; -} t_arg; - -typedef struct t_native_s -{ - char *name; - int16_t ret_tag; - uint16_t num_args; - t_arg args[sARGS_MAX]; -} t_native; - -t_native *native_list = NULL; +#include "sc.h" int main(int argc, char *argv[]) { - if (pc_compile(argc,argv) != 0) - return 1; - - AMX_HEADER *hdr; - AMX_DBG_HDR *dbg = NULL; - int err; - uint32_t i; - sp_file_t *spf; - memfile_t *dbgtab = NULL; //dbgcrab - unsigned char *dbgptr = NULL; - uint32_t sections[FS_Number] = {1,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0}; - FILE *fp; - - if (bin_file == NULL) - return 0; - - hdr = (AMX_HEADER *)bin_file->base; - - if ((spf=spfw_create(bin_file->name, NULL)) == NULL) { - pc_printf("Error creating binary file!\n"); - memfile_destroy(bin_file); - return 0; - } - - if ((err=setjmp(brkout))!=0) - goto write_error; - - spfw_add_section(spf, ".code"); - spfw_add_section(spf, ".data"); - - sections[FS_Publics] = (hdr->natives - hdr->publics) / hdr->defsize; - if (sections[FS_Publics]) - spfw_add_section(spf, ".publics"); - sections[FS_Pubvars] = (hdr->tags - hdr->pubvars) / hdr->defsize; - if (sections[FS_Pubvars]) - spfw_add_section(spf, ".pubvars"); - sections[FS_Natives] = (hdr->libraries - hdr->natives) / hdr->defsize; - if (sections[FS_Natives]) - spfw_add_section(spf, ".natives"); - sections[FS_Tags] = (hdr->nametable - hdr->tags) / hdr->defsize; - if (sections[FS_Tags]) - spfw_add_section(spf, ".tags"); - - spfw_add_section(spf, ".names"); - - if (hdr->flags & AMX_FLAG_DEBUG) { - dbg = (AMX_DBG_HDR *)((unsigned char *)hdr + hdr->size); - if (dbg->magic != AMX_DBG_MAGIC) { - pc_printf("Error reading AMX_DBG_HDR, debug data will not be written."); - } else { - dbgtab = memfile_creat("", 512); - dbgptr = (unsigned char *)dbg + sizeof(AMX_DBG_HDR); - if ((sections[FS_DbgNatives] = sections[FS_Natives]) > 0) - spfw_add_section(spf, ".dbg.natives"); - if (dbg->files) { - spfw_add_section(spf, ".dbg.files"); - sections[FS_DbgFile] = dbg->files; - } - if (dbg->lines) { - spfw_add_section(spf, ".dbg.lines"); - sections[FS_DbgLine] = dbg->lines; - } - if (dbg->symbols) { - spfw_add_section(spf, ".dbg.symbols"); - sections[FS_DbgSymbol] = dbg->symbols; - } - sections[FS_DbgInfo] = 1; - sections[FS_DbgStrings] = 1; - spfw_add_section(spf, ".dbg.info"); - spfw_add_section(spf, ".dbg.strings"); - } - } - - spfw_finalize_header(spf); - - /** - * Begin writing each of our known tables out - */ - - if (sections[FS_Code]) { - sp_file_code_t cod; - unsigned char *cbase; - - cod.cellsize = sizeof(cell); - - cod.codesize = hdr->dat - hdr->cod; - cod.codeversion = hdr->amx_version; - cod.flags = 0; - if (hdr->flags & AMX_FLAG_DEBUG) - { - cod.flags |= SP_FLAG_DEBUG; - } - cod.code = sizeof(cod); - cod.main = hdr->cip; - - /* write the code */ - cbase = (unsigned char *)hdr + hdr->cod; - sfwrite(&cod, sizeof(cod), 1, spf); - sfwrite(cbase, cod.codesize, 1, spf); - - spfw_next_section(spf); - } - - if (sections[FS_Data]) { - sp_file_data_t dat; - unsigned char *dbase = (unsigned char *)hdr + hdr->dat; - - dat.datasize = hdr->hea - hdr->dat; - dat.memsize = hdr->stp; - dat.data = sizeof(dat); - - /* write header */ - sfwrite(&dat, sizeof(dat), 1, spf); - - if (dat.datasize) { - /* write data */ - sfwrite(dbase, dat.datasize, 1, spf); - } - - spfw_next_section(spf); - } - - if (sections[FS_Publics]) { - sp_file_publics_t *pbtbl; - AMX_FUNCSTUBNT *stub; - unsigned char *stubptr; - uint32_t publics = sections[FS_Publics]; - - pbtbl = (sp_file_publics_t *)malloc(sizeof(sp_file_publics_t) * publics); - stubptr = (unsigned char *)hdr + hdr->publics; - - for (i=0; iaddress; - pbtbl[i].name = stub->nameofs - (hdr->nametable + sizeof(uint16_t)); - - stubptr += hdr->defsize; - } - if (publics) - sfwrite(pbtbl, sizeof(sp_file_publics_t), publics, spf); - free(pbtbl); - - spfw_next_section(spf); - } - - if (sections[FS_Pubvars]) { - sp_file_pubvars_t *pbvars; - AMX_FUNCSTUBNT *stub; - unsigned char *stubptr; - uint32_t pubvars = sections[FS_Pubvars]; - - pbvars = (sp_file_pubvars_t *)malloc(sizeof(sp_file_pubvars_t) * pubvars); - stubptr = (unsigned char *)hdr + hdr->pubvars; - - for (i=0; iaddress; - pbvars[i].name = stub->nameofs - (hdr->nametable + sizeof(uint16_t)); - - stubptr += hdr->defsize; - } - if (pubvars) - sfwrite(pbvars, sizeof(sp_file_pubvars_t), pubvars, spf); - free(pbvars); - spfw_next_section(spf); - } - - if (sections[FS_Natives]) { - sp_file_natives_t *nvtbl; - AMX_FUNCSTUBNT *stub; - unsigned char *stubptr; - uint32_t natives = (hdr->libraries - hdr->natives) / hdr->defsize; - - nvtbl = (sp_file_natives_t *)malloc(sizeof(sp_file_natives_t) * natives); - stubptr = (unsigned char *)hdr + hdr->natives; - - for (i=0; inameofs - (hdr->nametable + sizeof(uint16_t)); - - stubptr += hdr->defsize; - } - if (natives) { - sfwrite(nvtbl, sizeof(sp_file_natives_t), natives, spf); - } - free(nvtbl); - spfw_next_section(spf); - } - - if (sections[FS_Tags]) { - uint32_t numTags = (uint32_t)sections[FS_Tags]; - AMX_FUNCSTUBNT *stub; - sp_file_tag_t tag; - - for (i=0; itags + (i * hdr->defsize)); - tag.tag_id = stub->address; - tag.name = stub->nameofs - (hdr->nametable + sizeof(uint16_t)); - sfwrite(&tag, sizeof(sp_file_tag_t), 1, spf); - } - spfw_next_section(spf); - } - - if (sections[FS_Nametable]) { - unsigned char *base; - uint32_t namelen; - - /* write the entire block */ - base = (unsigned char *)hdr + hdr->nametable + sizeof(uint16_t); - /** - * note - the name table will be padded to sizeof(cell) bytes. - * this may clip at most an extra three bytes in! - */ - namelen = hdr->cod - hdr->nametable; - sfwrite(base, namelen, 1, spf); - spfw_next_section(spf); - } - - if (hdr->flags & AMX_FLAG_DEBUG) { - sp_fdbg_info_t info; - - memset(&info, 0, sizeof(sp_fdbg_info_t)); - - if (sections[FS_Natives]) { - uint16_t j; - uint32_t idx; - uint32_t name; - uint32_t natives = (hdr->libraries - hdr->natives) / hdr->defsize; - - sfwrite(&natives, sizeof(uint32_t), 1, spf); - for (idx=0; idxname); - /* store */ - dbgfile.addr = _ptr->address; - dbgfile.name = (uint32_t)memfile_tell(dbgtab); - sfwrite(&dbgfile, sizeof(sp_fdbg_file_t), 1, spf); - /* write to tab, then move to next */ - memfile_write(dbgtab, _ptr->name, len + 1); - dbgptr += sizeof(AMX_DBG_FILE) + len; - info.num_files++; - } - spfw_next_section(spf); - } - - if (sections[FS_DbgLine]) { - uint32_t idx; - AMX_DBG_LINE *line; - sp_fdbg_line_t dbgline; - for (idx=0; idxaddress; - dbgline.line = (uint32_t)line->line; - sfwrite(&dbgline, sizeof(sp_fdbg_line_t), 1, spf); - /* move to next */ - dbgptr += sizeof(AMX_DBG_LINE); - info.num_lines++; - } - spfw_next_section(spf); - } - - if (sections[FS_DbgSymbol]) { - uint32_t idx; - uint32_t dnum; - AMX_DBG_SYMBOL *sym; - AMX_DBG_SYMDIM *dim; - sp_fdbg_symbol_t dbgsym; - sp_fdbg_arraydim_t dbgdim; - uint32_t len; - - for (idx=0; idxaddress; - dbgsym.tagid = sym->tag; - dbgsym.codestart = (uint32_t)sym->codestart; - dbgsym.codeend = (uint32_t)sym->codeend; - dbgsym.dimcount = (uint16_t)sym->dim; - dbgsym.vclass = (uint8_t)sym->vclass; - dbgsym.ident = (uint8_t)sym->ident; - dbgsym.name = (uint32_t)memfile_tell(dbgtab); - sfwrite(&dbgsym, sizeof(sp_fdbg_symbol_t), 1, spf); - /* write to tab */ - len = strlen(sym->name); - memfile_write(dbgtab, sym->name, len + 1); - /* move to next */ - dbgptr += sizeof(AMX_DBG_SYMBOL) + len; - /* look for any dimensions */ - info.num_syms++; - for (dnum=0; dnumsize; - dbgdim.tagid = dim->tag; - sfwrite(&dbgdim, sizeof(sp_fdbg_arraydim_t), 1, spf); - /* move to next */ - dbgptr += sizeof(AMX_DBG_SYMDIM); - info.num_arrays++; - } - } - spfw_next_section(spf); - } - - sfwrite(&info, sizeof(sp_fdbg_info_t), 1, spf); - spfw_next_section(spf); - - if (sections[FS_DbgStrings]) { - sfwrite(dbgtab->base, sizeof(char), dbgtab->usedoffs, spf); - spfw_next_section(spf); - } - } - - spfw_finalize_all(spf); - - /** - * do compression - * new block for scoping only - */ - { - memfile_t *pOrig = (memfile_t *)spf->handle; - sp_file_hdr_t *pHdr; - unsigned char *proper; - size_t size; - Bytef *zcmp; - uLong disksize; - size_t header_size; - int err = Z_OK; - - /* reuse this memory block! */ - memfile_reset(bin_file); - - /* copy tip of header */ - memfile_write(bin_file, pOrig->base, sizeof(sp_file_hdr_t)); - - /* get pointer to header */ - pHdr = (sp_file_hdr_t *)bin_file->base; - - /* copy the rest of the header */ - memfile_write(bin_file, - (unsigned char *)pOrig->base + sizeof(sp_file_hdr_t), - pHdr->dataoffs - sizeof(sp_file_hdr_t)); - - header_size = pHdr->dataoffs; - size = pHdr->imagesize - header_size; - proper = (unsigned char *)pOrig->base + header_size; - - /* get initial size estimate */ - disksize = compressBound(pHdr->imagesize); - pHdr->disksize = (uint32_t)disksize; - zcmp = (Bytef *)malloc(pHdr->disksize); - - if ((err=compress2(zcmp, - &disksize, - (Bytef *)proper, - (uLong)size, - Z_BEST_COMPRESSION)) - != Z_OK) - { - free(zcmp); - pc_printf("Unable to compress (Z): error %d\n", err); - pc_printf("Falling back to no compression."); - memfile_write(bin_file, - proper, - size); - } else { - pHdr->disksize = (uint32_t)disksize + header_size; - pHdr->compression = SPFILE_COMPRESSION_GZ; - memfile_write(bin_file, - (unsigned char *)zcmp, - disksize); - free(zcmp); - } - } - - spfw_destroy(spf); - memfile_destroy(dbgtab); - - /** - * write file - */ - if ((fp=fopen(bin_file->name, "wb")) != NULL) { - fwrite(bin_file->base, bin_file->usedoffs, 1, fp); - fclose(fp); - } else { - pc_printf("Unable to open %s for writing!", bin_file->name); - } - - memfile_destroy(bin_file); - - return 0; - -write_error: - pc_printf("Error writing to file: %s", bin_file->name); - - spfw_destroy(spf); - unlink(bin_file->name); - memfile_destroy(bin_file); - memfile_destroy(dbgtab); - - return 1; + return pc_compile(argc,argv); } -void sfwrite(const void *buf, size_t size, size_t count, sp_file_t *spf) -{ - if (spf->funcs.fnWrite(buf, size, count, spf->handle) != count) - longjmp(brkout, 1); -} - -void sp_fdbg_ntv_start(int num_natives) -{ - if (num_natives == 0) - return; - - native_list = (t_native *)malloc(sizeof(t_native) * num_natives); - memset(native_list, 0, sizeof(t_native) * num_natives); -} - -#include "sc.h" -void sp_fdbg_ntv_hook(int index, symbol *sym) -{ - int i, j; - t_native *native; - - native = &native_list[index]; - native->name = strdup(sym->name); - - for (i = 0; i < sMAXARGS; i++) { - if (sym->dim.arglist[i].ident == 0) - break; - native->num_args++; - native->args[i].tag = sym->dim.arglist[i].tags == NULL ? 0 : sym->dim.arglist[i].tags[0]; - native->args[i].name = strdup(sym->dim.arglist[i].name); - native->args[i].ident = sym->dim.arglist[i].ident; - native->args[i].dimcount = sym->dim.arglist[i].numdim; - for (j = 0; j < native->args[i].dimcount; j++) { - native->args[i].dims[j].size = sym->dim.arglist[i].dim[j]; - native->args[i].dims[j].tagid = sym->dim.arglist[i].idxtag[j]; - } - } - - native->ret_tag = sym->tag; -} - - #if defined __linux__ || defined __APPLE__ extern "C" void __cxa_pure_virtual(void) { diff --git a/sourcepawn/compiler/sc.h b/sourcepawn/compiler/sc.h index aaab83c3..0a6b6fb5 100644 --- a/sourcepawn/compiler/sc.h +++ b/sourcepawn/compiler/sc.h @@ -578,13 +578,6 @@ void pc_resetasm(void *handle); int pc_writeasm(void *handle,const char *str); char *pc_readasm(void *handle,char *target,int maxchars); -/* output to binary (.AMX) file */ -void *pc_openbin(char *filename); -void pc_closebin(void *handle,int deletefile); -void pc_resetbin(void *handle,long offset); -int pc_writebin(void *handle,void *buffer,int size); -long pc_lengthbin(void *handle); /* return the length of the file */ - void sp_fdbg_ntv_start(int num_natives); void sp_fdbg_ntv_hook(int index, symbol *sym); @@ -767,7 +760,7 @@ int error(int number,...); void errorset(int code,int line); /* function prototypes in SC6.C */ -int assemble(void *fout,void *fin); +void assemble(const char *outname, void *fin); /* function prototypes in SC7.C */ void stgbuffer_cleanup(void); diff --git a/sourcepawn/compiler/sc1.cpp b/sourcepawn/compiler/sc1.cpp index bd543af3..95114951 100644 --- a/sourcepawn/compiler/sc1.cpp +++ b/sourcepawn/compiler/sc1.cpp @@ -200,16 +200,11 @@ int pc_compile(int argc, char *argv[]) char incfname[_MAX_PATH]; char reportname[_MAX_PATH]; char codepage[MAXCODEPAGE+1]; - FILE *binf; void *inpfmark; int lcl_packstr,lcl_needsemicolon,lcl_tabsize,lcl_require_newdecls; - #if !defined SC_LIGHT - int hdrsize=0; - #endif char *ptr; /* set global variables to their initial value */ - binf=NULL; initglobals(); errorset(sRESET,0); errorset(sEXPRRELEASE,0); @@ -315,14 +310,6 @@ int pc_compile(int argc, char *argv[]) outf=(FILE*)pc_openasm(outfname); /* first write to assembler file (may be temporary) */ if (outf==NULL) error(161,outfname); - /* immediately open the binary file, for other programs to check */ - if (sc_asmfile || sc_listing) { - binf=NULL; - } else { - binf=(FILE*)pc_openbin(binfname); - if (binf==NULL) - error(161,binfname); - } /* if */ setconstants(); /* set predefined constants and tagnames */ for (i=0; i0) { - long totalsize=hdrsize+code_idx; + long totalsize=code_idx; if (pc_amxram==0) totalsize+=(glb_declared+pc_stksize)*sizeof(cell); if (totalsize>=pc_amxlimit) @@ -498,20 +479,10 @@ cleanup: if (pc_amxram>0 && (glb_declared+pc_stksize)*sizeof(cell)>=(unsigned long)pc_amxram) flag_exceed=1; if ((!norun && (sc_debug & sSYMBOLIC)!=0) || verbosity>=2 || flag_exceed) { - pc_printf("Header size: %8ld bytes\n", (long)hdrsize); pc_printf("Code size: %8ld bytes\n", (long)code_idx); pc_printf("Data size: %8ld bytes\n", (long)glb_declared*sizeof(cell)); pc_printf("Stack/heap size: %8ld bytes\n", (long)pc_stksize*sizeof(cell)); -#if 0 - pc_printf("estimated max. usage"); - if (recursion) - pc_printf(": unknown, due to recursion\n"); - else if ((pc_memflags & suSLEEP_INSTR)!=0) - pc_printf(": unknown, due to the \"sleep\" instruction\n"); - else - pc_printf("=%ld cells (%ld bytes)\n",stacksize,stacksize*sizeof(cell)); -#endif - pc_printf("Total requirements:%8ld bytes\n", (long)hdrsize+(long)code_idx+(long)glb_declared*sizeof(cell)+(long)pc_stksize*sizeof(cell)); + pc_printf("Total requirements:%8ld bytes\n", (long)code_idx+(long)glb_declared*sizeof(cell)+(long)pc_stksize*sizeof(cell)); } /* if */ if (flag_exceed) error(166,pc_amxlimit+pc_amxram); /* this causes a jump back to label "cleanup" */ diff --git a/sourcepawn/compiler/sc6.cpp b/sourcepawn/compiler/sc6.cpp index 8e677f0e..88acfe30 100644 --- a/sourcepawn/compiler/sc6.cpp +++ b/sourcepawn/compiler/sc6.cpp @@ -1,3 +1,4 @@ +// vim: set sts=2 ts=8 sw=2 tw=99 et: /* Pawn compiler - Binary code generation (the "assembler") * * Copyright (c) ITB CompuPhase, 1997-2006 @@ -35,12 +36,18 @@ #if defined LINUX || defined __FreeBSD__ || defined __OpenBSD__ #include #endif +#include +#include +#include +#include "smx-builder.h" +#include "memory-buffer.h" +using namespace sp; +using namespace ke; static void append_dbginfo(void *fout); - -typedef cell (*OPCODE_PROC)(void *fbin,char *params,cell opcode); +typedef cell (*OPCODE_PROC)(Vector *buffer, char *params, cell opcode); typedef struct { cell opcode; @@ -50,7 +57,7 @@ typedef struct { } OPCODEC; static cell codeindex; /* similar to "code_idx" */ -static cell *lbltab; /* label table */ +static cell *LabelTable; /* label table */ static int writeerror; static int bytes_in, bytes_out; static jmp_buf compact_err; @@ -107,74 +114,6 @@ static ucell getparam(const char *s,char **n) return result; } -#if BYTE_ORDER==BIG_ENDIAN -static uint16_t *align16(uint16_t *v) -{ - unsigned char *s = (unsigned char *)v; - unsigned char t; - - /* swap two bytes */ - t=s[0]; - s[0]=s[1]; - s[1]=t; - return v; -} - -static uint32_t *align32(uint32_t *v) -{ - unsigned char *s = (unsigned char *)v; - unsigned char t; - - /* swap outer two bytes */ - t=s[0]; - s[0]=s[3]; - s[3]=t; - /* swap inner two bytes */ - t=s[1]; - s[1]=s[2]; - s[2]=t; - return v; -} - -#if PAWN_CELL_SIZE>=64 -static uint64_t *align64(uint64_t *v) -{ - unsigned char *s = (unsigned char *)v; - unsigned char t; - - t=s[0]; - s[0]=s[7]; - s[7]=t; - - t=s[1]; - s[1]=s[6]; - s[6]=t; - - t=s[2]; - s[2]=s[5]; - s[5]=t; - - t=s[3]; - s[3]=s[4]; - s[4]=t; - - return v; -} -#endif - - #if PAWN_CELL_SIZE==16 - #define aligncell(v) align16(v) - #elif PAWN_CELL_SIZE==32 - #define aligncell(v) align32(v) - #elif PAWN_CELL_SIZE==64 - #define aligncell(v) align64(v) - #endif -#else - #define align16(v) (v) - #define align32(v) (v) - #define aligncell(v) (v) -#endif - static char *skipwhitespace(char *str) { while (isspace(*str)) @@ -192,266 +131,178 @@ static char *stripcomment(char *str) return str; } -static void write_encoded(void *fbin,ucell *c,int num) -{ - #if PAWN_CELL_SIZE == 16 - #define ENC_MAX 3 /* a 16-bit cell is encoded in max. 3 bytes */ - #define ENC_MASK 0x03 /* after 2x7 bits, 2 bits remain to make 16 bits */ - #elif PAWN_CELL_SIZE == 32 - #define ENC_MAX 5 /* a 32-bit cell is encoded in max. 5 bytes */ - #define ENC_MASK 0x0f /* after 4x7 bits, 4 bits remain to make 32 bits */ - #elif PAWN_CELL_SIZE == 64 - #define ENC_MAX 10 /* a 32-bit cell is encoded in max. 10 bytes */ - #define ENC_MASK 0x01 /* after 9x7 bits, 1 bit remains to make 64 bits */ - #endif - - assert(fbin!=NULL); - while (num-->0) { - if (sc_compress) { - ucell p=(ucell)*c; - unsigned char t[ENC_MAX]; - unsigned char code; - int index; - for (index=0; index>=7; - } /* for */ - /* skip leading zeros */ - while (index>1 && t[index-1]==0 && (t[index-2] & 0x40)==0) - index--; - /* skip leading -1s */ - if (index==ENC_MAX && t[index-1]==ENC_MASK && (t[index-2] & 0x40)!=0) - index--; - while (index>1 && t[index-1]==0x7f && (t[index-2] & 0x40)!=0) - index--; - /* write high byte first, write continuation bits */ - assert(index>0); - while (index-->0) { - code=(unsigned char)((index==0) ? t[index] : (t[index]|0x80)); - writeerror |= !pc_writebin(fbin,&code,1); - bytes_out++; - } /* while */ - bytes_in+=sizeof *c; - assert(AMX_COMPACTMARGIN>2); - if (bytes_out-bytes_in>=AMX_COMPACTMARGIN-2) - longjmp(compact_err,1); - } else { - assert((pc_lengthbin(fbin) % sizeof(cell)) == 0); - writeerror |= !pc_writebin(fbin,aligncell(c),sizeof *c); - } /* if */ - c++; - } /* while */ -} - -#if defined __BORLANDC__ || defined __WATCOMC__ - #pragma argsused -#endif -static cell noop(void *fbin,char *params,cell opcode) +static cell noop(Vector *buffer, char *params, cell opcode) { return 0; } -#if defined __BORLANDC__ || defined __WATCOMC__ - #pragma argsused -#endif -static cell set_currentfile(void *fbin,char *params,cell opcode) +static cell set_currentfile(Vector *buffer, char *params, cell opcode) { fcurrent=(short)getparam(params,NULL); return 0; } -#if defined __BORLANDC__ || defined __WATCOMC__ - #pragma argsused -#endif -static cell parm0(void *fbin,char *params,cell opcode) +static cell parm0(Vector *buffer, char *params, cell opcode) { - if (fbin!=NULL) - write_encoded(fbin,(ucell*)&opcode,1); + if (buffer) + buffer->append(opcode); return opcodes(1); } -static cell parm1(void *fbin,char *params,cell opcode) +static cell parm1(Vector *buffer, char *params, cell opcode) { - ucell p=getparam(params,NULL); - if (fbin!=NULL) { - write_encoded(fbin,(ucell*)&opcode,1); - write_encoded(fbin,&p,1); - } /* if */ - return opcodes(1)+opargs(1); + ucell p = getparam(params, nullptr); + if (buffer) { + buffer->append(opcode); + buffer->append(p); + } + return opcodes(1) + opargs(1); } -static cell parm2(void *fbin,char *params,cell opcode) +static cell parm2(Vector *buffer, char *params, cell opcode) { - ucell p1=getparam(params,¶ms); - ucell p2=getparam(params,NULL); - if (fbin!=NULL) { - write_encoded(fbin,(ucell*)&opcode,1); - write_encoded(fbin,&p1,1); - write_encoded(fbin,&p2,1); - } /* if */ - return opcodes(1)+opargs(2); + ucell p1 = getparam(params, ¶ms); + ucell p2 = getparam(params, nullptr); + if (buffer) { + buffer->append(opcode); + buffer->append(p1); + buffer->append(p2); + } + return opcodes(1) + opargs(2); } -static cell parm3(void *fbin,char *params,cell opcode) +static cell parm3(Vector *buffer, char *params, cell opcode) { - ucell p1=getparam(params,¶ms); - ucell p2=getparam(params,¶ms); - ucell p3=getparam(params,NULL); - if (fbin!=NULL) { - write_encoded(fbin,(ucell*)&opcode,1); - write_encoded(fbin,&p1,1); - write_encoded(fbin,&p2,1); - write_encoded(fbin,&p3,1); - } /* if */ - return opcodes(1)+opargs(3); + ucell p1 = getparam(params, ¶ms); + ucell p2 = getparam(params, ¶ms); + ucell p3 = getparam(params, nullptr); + if (buffer) { + buffer->append(opcode); + buffer->append(p1); + buffer->append(p2); + buffer->append(p3); + } + return opcodes(1) + opargs(3); } -static cell parm4(void *fbin,char *params,cell opcode) +static cell parm4(Vector *buffer, char *params, cell opcode) { - ucell p1=getparam(params,¶ms); - ucell p2=getparam(params,¶ms); - ucell p3=getparam(params,¶ms); - ucell p4=getparam(params,NULL); - if (fbin!=NULL) { - write_encoded(fbin,(ucell*)&opcode,1); - write_encoded(fbin,&p1,1); - write_encoded(fbin,&p2,1); - write_encoded(fbin,&p3,1); - write_encoded(fbin,&p4,1); - } /* if */ - return opcodes(1)+opargs(4); + ucell p1 = getparam(params, ¶ms); + ucell p2 = getparam(params, ¶ms); + ucell p3 = getparam(params, ¶ms); + ucell p4 = getparam(params, nullptr); + if (buffer) { + buffer->append(opcode); + buffer->append(p1); + buffer->append(p2); + buffer->append(p3); + buffer->append(p4); + } + return opcodes(1) + opargs(4); } -static cell parm5(void *fbin,char *params,cell opcode) +static cell parm5(Vector *buffer, char *params, cell opcode) { - ucell p1=getparam(params,¶ms); - ucell p2=getparam(params,¶ms); - ucell p3=getparam(params,¶ms); - ucell p4=getparam(params,¶ms); - ucell p5=getparam(params,NULL); - if (fbin!=NULL) { - write_encoded(fbin,(ucell*)&opcode,1); - write_encoded(fbin,&p1,1); - write_encoded(fbin,&p2,1); - write_encoded(fbin,&p3,1); - write_encoded(fbin,&p4,1); - write_encoded(fbin,&p5,1); - } /* if */ - return opcodes(1)+opargs(5); + ucell p1 = getparam(params, ¶ms); + ucell p2 = getparam(params, ¶ms); + ucell p3 = getparam(params, ¶ms); + ucell p4 = getparam(params, ¶ms); + ucell p5 = getparam(params, nullptr); + if (buffer) { + buffer->append(opcode); + buffer->append(p1); + buffer->append(p2); + buffer->append(p3); + buffer->append(p4); + buffer->append(p5); + } + return opcodes(1) + opargs(5); } -#if defined __BORLANDC__ || defined __WATCOMC__ - #pragma argsused -#endif -static cell do_dump(void *fbin,char *params,cell opcode) +static cell do_dump(Vector *buffer, char *params, cell opcode) { - ucell p; int num = 0; - while (*params!='\0') { - p=getparam(params,¶ms); - if (fbin!=NULL) - write_encoded(fbin,&p,1); + while (*params != '\0') { + ucell p = getparam(params, ¶ms); + if (buffer) + buffer->append(p); num++; while (isspace(*params)) params++; - } /* while */ - return num*sizeof(cell); + } + return num * sizeof(cell); } -static cell do_call(void *fbin,char *params,cell opcode) +static cell do_call(Vector *buffer, char *params, cell opcode) { char name[sNAMEMAX+1]; - int i; - symbol *sym; - ucell p; + int i; for (i=0; !isspace(*params); i++,params++) { - assert(*params!='\0'); - assert(i=0 && i= 0 && val < sc_labnum); + p = LabelTable[val]; } else { - /* look up the function address; note that the correct file number must - * already have been set (in order for static globals to be found). - */ - sym=findglb(name,sGLOBAL); - assert(sym!=NULL); - assert(sym->ident==iFUNCTN || sym->ident==iREFFUNC); - assert(sym->vclass==sGLOBAL); - p=sym->addr; - } /* if */ + // Look up the function address; note that the correct file number must + // already have been set (in order for static globals to be found). + symbol *sym = findglb(name, sGLOBAL); + assert(sym->ident == iFUNCTN || sym->ident == iREFFUNC); + assert(sym->vclass == sGLOBAL); + p = sym->addr; + } - if (fbin!=NULL) { - write_encoded(fbin,(ucell*)&opcode,1); - write_encoded(fbin,&p,1); - } /* if */ - return opcodes(1)+opargs(1); + if (buffer) { + buffer->append(opcode); + buffer->append(p); + } + return opcodes(1) + opargs(1); } -static cell do_jump(void *fbin,char *params,cell opcode) +static cell do_jump(Vector *buffer, char *params, cell opcode) { - int i; - ucell p; + int i = (int)hex2long(params, nullptr); + assert(i >= 0 && i < sc_labnum); - i=(int)hex2long(params,NULL); - assert(i>=0 && iappend(opcode); + buffer->append(LabelTable[i]); + } + return opcodes(1) + opargs(1); } -static cell do_switch(void *fbin,char *params,cell opcode) +static cell do_switch(Vector *buffer, char *params, cell opcode) { - int i; - ucell p; + int i = (int)hex2long(params, nullptr); + assert(i >= 0 && i < sc_labnum); - i=(int)hex2long(params,NULL); - assert(i>=0 && iappend(opcode); + buffer->append(LabelTable[i]); + } + return opcodes(1) + opargs(1); } -#if defined __BORLANDC__ || defined __WATCOMC__ - #pragma argsused -#endif -static cell do_case(void *fbin,char *params,cell opcode) +static cell do_case(Vector *buffer, char *params, cell opcode) { - int i; - ucell p,v; + cell v = hex2long(params ,¶ms); + int i = (int)hex2long(params, nullptr); + assert(i >= 0 && i < sc_labnum); - v=hex2long(params,¶ms); - i=(int)hex2long(params,NULL); - assert(i>=0 && iappend(v); + buffer->append(LabelTable[i]); + } + return opcodes(0) + opargs(2); } static OPCODEC opcodelist[] = { @@ -659,403 +510,275 @@ static int findopcode(char *instr,int maxlen) return 0; /* not found, return special index */ } -int assemble(void *fout, void *fin) +// This pass is necessary because the code addresses of labels is only known +// after the peephole optimization flag. Labels can occur inside expressions +// (e.g. the conditional operator), which are optimized. +static void relocate_labels(void *fin) { - AMX_HEADER hdr; - AMX_FUNCSTUBNT func; - int numpublics,numnatives,numlibraries,numpubvars,numtags,padding; - long nametablesize,nameofs; - #if PAWN_CELL_SIZE > 32 - char line[512]; - #else - char line[256]; - #endif - char *instr,*params; - int i,pass,size; - int16_t count; - symbol *sym, **nativelist; - constvalue *constptr; - cell mainaddr; - char nullchar; - char testalias[sNAMEMAX+1]; + if (sc_labnum <= 0) + return; - /* if compression failed, restart the assembly with compaction switched off */ - if (setjmp(compact_err)!=0) { - assert(sc_compress); /* cannot arrive here if compact encoding was disabled */ - sc_compress=FALSE; - pc_resetbin(fout,0); - error(232); /* disabled compact encoding */ - } /* if */ + assert(!LabelTable); + LabelTable = (cell *)calloc(sc_labnum, sizeof(cell)); - #if !defined NDEBUG - /* verify that the opcode list is sorted (skip entry 1; it is reserved - * for a non-existant opcode) - */ + char line[256]; + cell codeindex = 0; + + pc_resetasm(fin); + while (pc_readasm(fin, line, sizeof(line))) { + stripcomment(line); + + char *instr = skipwhitespace(line); + if (*instr == '\0') // Ignore empty lines. + continue; + + if (tolower(*instr) == 'l' && *(instr + 1) == '.') { + int lindex = (int)hex2long(instr + 2, nullptr); + assert(lindex >= 0 && lindex < sc_labnum); + LabelTable[lindex] = codeindex; + } else { + // Get to the end of the instruction (make use of the '\n' that fgets() + // added at the end of the line; this way we *always* drop on a whitespace + // character. + char *params; + for (params = instr; *params != '\0' && !isspace(*params); params++) { + // Nothing. + } + assert(params > instr); + + int op_index = findopcode(instr, (int)(params - instr)); + OPCODEC &op = opcodelist[op_index]; + if (!op.name) { + *params = '\0'; + error(104, instr); + } + + if (op.segment == sIN_CSEG) + codeindex += op.func(nullptr, skipwhitespace(params), op.opcode); + } + } +} + +// Generate code or data into a buffer. +static void generate_segment(Vector *buffer, void *fin, int pass) +{ + pc_resetasm(fin); + + char line[255]; + while (pc_readasm(fin, line, sizeof(line))) { + stripcomment(line); + char *instr = skipwhitespace(line); + + // Ignore empty lines and labels. + if (*instr=='\0' || (tolower(*instr) == 'l' && *(instr + 1)=='.')) + continue; + + // Get to the end of the instruction (make use of the '\n' that fgets() + // added at the end of the line; this way we will *always* drop on a + // whitespace character) */ + char *params; + for (params=instr; *params != '\0' && !isspace(*params); params++) { + // Do nothing. + } + assert(params > instr); + + int op_index = findopcode(instr, (int)(params-instr)); + OPCODEC &op = opcodelist[op_index]; + assert(op.name != nullptr); + + if (op.segment != pass) + continue; + + op.func(buffer, skipwhitespace(params), op.opcode); + } +} + +#if !defined NDEBUG +// The opcode list should be sorted by name. +class VerifyOpcodeSorting +{ + public: + VerifyOpcodeSorting() { assert(opcodelist[1].name!=NULL); - for (i=2; i<(sizeof opcodelist / sizeof opcodelist[0]); i++) { + for (int i = 2; i<(sizeof opcodelist / sizeof opcodelist[0]); i++) { assert(opcodelist[i].name!=NULL); assert(stricmp(opcodelist[i].name,opcodelist[i-1].name)>0); } /* for */ - #endif + } +} sVerifyOpcodeSorting; +#endif - writeerror=FALSE; - nametablesize=sizeof(int16_t); - numpublics=0; - numnatives=0; - numpubvars=0; - mainaddr=-1; - /* count number of public and native functions and public variables */ - for (sym=glbtab.next; sym!=NULL; sym=sym->next) { - int match=0; +static int sort_by_addr(const void *a1, const void *a2) +{ + symbol *s1 = *(symbol **)a1; + symbol *s2 = *(symbol **)a2; + return s1->addr - s2->addr; +} + +typedef SmxListSection SmxNativeSection; +typedef SmxListSection SmxPublicSection; +typedef SmxListSection SmxPubvarSection; +typedef SmxListSection SmxTagSection; +typedef SmxBlobSection SmxDataSection; +typedef SmxBlobSection SmxCodeSection; + +static void assemble_to_buffer(MemoryBuffer *buffer, void *fin) +{ + StringPool pool; + SmxBuilder builder; + 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"); + + Vector nativeList; + + // Build the easy symbol tables. + for (symbol *sym=glbtab.next; sym; sym=sym->next) { if (sym->ident==iFUNCTN) { - if ((sym->usage & uNATIVE)!=0 && (sym->usage & uREAD)!=0 && sym->addr>=0) { - match=++numnatives; + if ((sym->usage & uNATIVE)!=0 && (sym->usage & uREAD)!=0 && sym->addr >= 0) { + // Natives require special handling, so we save them for later. + nativeList.append(sym); + } else if ((sym->usage & uPUBLIC)!=0 && (sym->usage & uDEFINE)!=0) { + sp_file_publics_t &pubfunc = publics->add(); + pubfunc.address = sym->addr; + pubfunc.name = names->add(pool, sym->name); } - if ((sym->usage & uPUBLIC)!=0 && (sym->usage & uDEFINE)!=0) - match=++numpublics; - if (strcmp(sym->name,uMAINFUNC)==0) { - assert(sym->vclass==sGLOBAL); - mainaddr=sym->addr; - } /* if */ } else if (sym->ident==iVARIABLE || sym->ident == iARRAY || sym->ident == iREFARRAY) { - if ((sym->usage & uPUBLIC)!=0 && (sym->usage & (uREAD | uWRITTEN))!=0) - match=++numpubvars; - } /* if */ - if (match) { - const char *aliasptr = sym->name; - assert(sym!=NULL); - if (((sym->usage & uNATIVE)!=0) && lookup_alias(testalias,sym->name)) { - aliasptr = "@"; - } /* if */ - nametablesize+=strlen(aliasptr)+1; - } /* if */ - } /* for */ - assert(numnatives==ntv_funcid); - - /* count number of libraries */ - numlibraries=0; - if (pc_addlibtable) { - for (constptr=libname_tab.next; constptr!=NULL; constptr=constptr->next) { - if (constptr->value>0) { - assert(strlen(constptr->name)>0); - numlibraries++; - nametablesize+=strlen(constptr->name)+1; - } /* if */ - } /* for */ - } /* if */ - - /* count number of public tags */ - numtags=0; - for (constptr=tagname_tab.next; constptr!=NULL; constptr=constptr->next) { - /*if ((constptr->value & PUBLICTAG)!=0) {*/ - assert(strlen(constptr->name)>0); - numtags++; - nametablesize+=strlen(constptr->name)+1; - /*} if */ - } /* for */ - - /* pad the header to sc_dataalign - * => thereby the code segment is aligned - * => since the code segment is padded to a sc_dataalign boundary, the data segment is aligned - * => and thereby the stack top is aligned too - */ - assert(sc_dataalign!=0); - padding= (int)(sc_dataalign - (sizeof hdr + nametablesize) % sc_dataalign); - if (padding==sc_dataalign) - padding=0; - - /* write the abstract machine header */ - memset(&hdr, 0, sizeof hdr); - hdr.magic=(unsigned short)AMX_MAGIC; - hdr.file_version=(char)((pc_optimize<=sOPTIMIZE_NOMACRO) ? MAX_FILE_VER_JIT : CUR_FILE_VERSION); - hdr.amx_version=(char)((pc_optimize<=sOPTIMIZE_NOMACRO) ? MIN_AMX_VER_JIT : MIN_AMX_VERSION); - hdr.flags=(short)(sc_debug & sSYMBOLIC); - if (sc_compress) - hdr.flags|=AMX_FLAG_COMPACT; - if (sc_debug==0) - hdr.flags|=AMX_FLAG_NOCHECKS; - if (pc_memflags & suSLEEP_INSTR) - hdr.flags|=AMX_FLAG_SLEEP; - hdr.defsize=sizeof(AMX_FUNCSTUBNT); - hdr.publics=sizeof hdr; /* public table starts right after the header */ - hdr.natives=hdr.publics + numpublics*sizeof(AMX_FUNCSTUBNT); - hdr.libraries=hdr.natives + numnatives*sizeof(AMX_FUNCSTUBNT); - hdr.pubvars=hdr.libraries + numlibraries*sizeof(AMX_FUNCSTUBNT); - hdr.tags=hdr.pubvars + numpubvars*sizeof(AMX_FUNCSTUBNT); - hdr.nametable=hdr.tags + numtags*sizeof(AMX_FUNCSTUBNT); - hdr.cod=hdr.nametable + nametablesize + padding; - hdr.dat=hdr.cod + code_idx; - hdr.hea=hdr.dat + glb_declared*sizeof(cell); - hdr.stp=hdr.hea + pc_stksize*sizeof(cell); - hdr.cip=mainaddr; - hdr.size=hdr.hea; /* preset, this is incorrect in case of compressed output */ - pc_writebin(fout,&hdr,sizeof hdr); - - /* dump zeros up to the rest of the header, so that we can easily "seek" */ - nullchar='\0'; - for (nameofs=sizeof hdr; nameofsnext) { - if (sym->ident==iFUNCTN - && (sym->usage & uPUBLIC)!=0 && (sym->usage & uDEFINE)!=0) - { - assert(sym->vclass==sGLOBAL); - func.address=sym->addr; - func.nameofs=nameofs; - #if BYTE_ORDER==BIG_ENDIAN - align32(&func.address); - align32(&func.nameofs); - #endif - pc_resetbin(fout,hdr.publics+count*sizeof(AMX_FUNCSTUBNT)); - pc_writebin(fout,&func,sizeof func); - pc_resetbin(fout,nameofs); - pc_writebin(fout,sym->name,strlen(sym->name)+1); - nameofs+=strlen(sym->name)+1; - count++; - } /* if */ - } /* for */ - - /* write the natives table */ - /* The native functions must be written in sorted order. (They are - * sorted on their "id", not on their name). A nested loop to find - * each successive function would be an O(n^2) operation. But we - * do not really need to sort, because the native function id's - * are sequential and there are no duplicates. So we first walk - * through the complete symbol list and store a pointer to every - * native function of interest in a temporary table, where its id - * serves as the index in the table. Now we can walk the table and - * have all native functions in sorted order. - */ - if (numnatives>0) { - nativelist=(symbol **)malloc(numnatives*sizeof(symbol *)); - if (nativelist==NULL) - error(103); /* insufficient memory */ - #if !defined NDEBUG - memset(nativelist,0,numnatives*sizeof(symbol *)); /* for NULL checking */ - #endif - for (sym=glbtab.next; sym!=NULL; sym=sym->next) { - if (sym->ident==iFUNCTN && (sym->usage & uNATIVE)!=0 && (sym->usage & uREAD)!=0 && sym->addr>=0) { - assert(sym->addr < numnatives); - nativelist[(int)sym->addr]=sym; - } /* if */ - } /* for */ - count=0; - sp_fdbg_ntv_start(numnatives); - for (i=0; iname; - if (lookup_alias(testalias,sym->name)) { - aliasptr = "@"; + if ((sym->usage & uPUBLIC)!=0 && (sym->usage & (uREAD | uWRITTEN))!=0) { + sp_file_pubvars_t &pubvar = pubvars->add(); + pubvar.address = sym->addr; + pubvar.name = names->add(pool, sym->name); } - assert(sym->vclass==sGLOBAL); - func.address=0; - func.nameofs=nameofs; - #if BYTE_ORDER==BIG_ENDIAN - align32(&func.address); - align32(&func.nameofs); - #endif - pc_resetbin(fout,hdr.natives+count*sizeof(AMX_FUNCSTUBNT)); - pc_writebin(fout,&func,sizeof func); - pc_resetbin(fout,nameofs); - pc_writebin(fout,(void *)aliasptr,strlen(aliasptr)+1); - nameofs+=strlen(aliasptr)+1; - count++; - } /* for */ - free(nativelist); - } /* if */ + } + } - /* write the libraries table */ - if (pc_addlibtable) { - count=0; - for (constptr=libname_tab.next; constptr!=NULL; constptr=constptr->next) { - if (constptr->value>0) { - assert(strlen(constptr->name)>0); - func.address=0; - func.nameofs=nameofs; - #if BYTE_ORDER==BIG_ENDIAN - align32(&func.address); - align32(&func.nameofs); - #endif - pc_resetbin(fout,hdr.libraries+count*sizeof(AMX_FUNCSTUBNT)); - pc_writebin(fout,&func,sizeof func); - pc_resetbin(fout,nameofs); - pc_writebin(fout,constptr->name,strlen(constptr->name)+1); - nameofs+=strlen(constptr->name)+1; - count++; - } /* if */ - } /* for */ - } /* if */ + // Build the tags table. + for (constvalue *constptr = tagname_tab.next; constptr; constptr = constptr->next) { + assert(strlen(constptr->name)>0); - /* write the public variables table */ - count=0; - for (sym=glbtab.next; sym!=NULL; sym=sym->next) { - if ((sym->ident==iVARIABLE || sym->ident==iARRAY || sym->ident==iREFARRAY) - && (sym->usage & uPUBLIC)!=0 && (sym->usage & (uREAD | uWRITTEN))!=0) { - //removed until structs don't seem to mess this up - //assert((sym->usage & uDEFINE)!=0); - assert(sym->vclass==sGLOBAL); - func.address=sym->addr; - func.nameofs=nameofs; - #if BYTE_ORDER==BIG_ENDIAN - align32(&func.address); - align32(&func.nameofs); - #endif - pc_resetbin(fout,hdr.pubvars+count*sizeof(AMX_FUNCSTUBNT)); - pc_writebin(fout,&func,sizeof func); - pc_resetbin(fout,nameofs); - pc_writebin(fout,sym->name,strlen(sym->name)+1); - nameofs+=strlen(sym->name)+1; - count++; - } /* if */ - } /* for */ + sp_file_tag_t &tag = tags->add(); + tag.tag_id = constptr->value; + tag.name = names->add(pool, constptr->name); + } - /* write the public tagnames table */ - count=0; - for (constptr=tagname_tab.next; constptr!=NULL; constptr=constptr->next) { - /*if ((constptr->value & PUBLICTAG)!=0) {*/ - assert(strlen(constptr->name)>0); - func.address=constptr->value & TAGMASK; - func.nameofs=nameofs; - #if BYTE_ORDER==BIG_ENDIAN - align32(&func.address); - align32(&func.nameofs); - #endif - pc_resetbin(fout,hdr.tags+count*sizeof(AMX_FUNCSTUBNT)); - pc_writebin(fout,&func,sizeof func); - pc_resetbin(fout,nameofs); - pc_writebin(fout,constptr->name,strlen(constptr->name)+1); - nameofs+=strlen(constptr->name)+1; - count++; - /*} if */ - } /* for */ + // 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++) { + symbol *sym = nativeList[i]; + assert(size_t(sym->addr) == i); - /* write the "maximum name length" field in the name table */ - assert(nameofs==hdr.nametable+nametablesize); - pc_resetbin(fout,hdr.nametable); - count=sNAMEMAX; - #if BYTE_ORDER==BIG_ENDIAN - align16(&count); - #endif - pc_writebin(fout,&count,sizeof count); - pc_resetbin(fout,hdr.cod); + sp_file_natives_t &entry = natives->add(); - /* First pass: relocate all labels */ - /* This pass is necessary because the code addresses of labels is only known - * after the peephole optimization flag. Labels can occur inside expressions - * (e.g. the conditional operator), which are optimized. - */ - lbltab=NULL; - if (sc_labnum>0) { - /* only very short programs have zero labels; no first pass is needed - * if there are no labels */ - lbltab=(cell *)malloc(sc_labnum*sizeof(cell)); - if (lbltab==NULL) - error(103); /* insufficient memory */ - codeindex=0; - pc_resetasm(fin); - while (pc_readasm(fin,line,sizeof line)!=NULL) { - stripcomment(line); - instr=skipwhitespace(line); - /* ignore empty lines */ - if (*instr=='\0') - continue; - if (tolower(*instr)=='l' && *(instr+1)=='.') { - int lindex=(int)hex2long(instr+2,NULL); - assert(lindex>=0 && lindexinstr); - i=findopcode(instr,(int)(params-instr)); - if (opcodelist[i].name==NULL) { - *params='\0'; - error(104,instr); /* invalid assembler instruction */ - } /* if */ - if (opcodelist[i].segment==sIN_CSEG) - codeindex+=opcodelist[i].func(NULL,skipwhitespace(params),opcodelist[i].opcode); - } /* if */ - } /* while */ - } /* if */ + char testalias[sNAMEMAX + 1]; + if (lookup_alias(testalias, sym->name)) + entry.name = names->add(pool, "@"); + else + entry.name = names->add(pool, sym->name); + } - /* Second pass (actually 2 more passes, one for all code and one for all data) */ - bytes_in=0; - bytes_out=0; - for (pass=sIN_CSEG; pass<=sIN_DSEG; pass++) { - pc_resetasm(fin); - while (pc_readasm(fin,line,sizeof line)!=NULL) { - stripcomment(line); - instr=skipwhitespace(line); - /* ignore empty lines and labels (labels have a special syntax, so these - * must be parsed separately) */ - if (*instr=='\0' || (tolower(*instr)=='l' && *(instr+1)=='.')) - continue; - /* get to the end of the instruction (make use of the '\n' that fgets() - * added at the end of the line; this way we will *always* drop on a - * whitespace character) */ - for (params=instr; *params!='\0' && !isspace(*params); params++) - /* nothing */; - assert(params>instr); - i=findopcode(instr,(int)(params-instr)); - assert(opcodelist[i].name!=NULL); - if (opcodelist[i].segment==pass) - opcodelist[i].func(fout,skipwhitespace(params),opcodelist[i].opcode); - } /* while */ - } /* for */ - if (bytes_out-bytes_in>0) - error(106); /* compression buffer overflow */ + // Relocate all labels in the assembly buffer. + relocate_labels(fin); - if (lbltab!=NULL) { - free(lbltab); - #if !defined NDEBUG - lbltab=NULL; - #endif - } /* if */ + // Generate buffers. + Vector code_buffer, data_buffer; + generate_segment(&code_buffer, fin, sIN_CSEG); + generate_segment(&data_buffer, fin, sIN_DSEG); - if (sc_compress) - hdr.size=pc_lengthbin(fout);/* get this value before appending debug info */ - if (!writeerror && (sc_debug & sSYMBOLIC)!=0) - append_dbginfo(fout); /* optionally append debug file */ + // Set up the code section. + code->header().codesize = code_buffer.length() * sizeof(cell); + code->header().cellsize = sizeof(cell); + code->header().codeversion = SmxConsts::CODE_VERSION_JIT2; + code->header().flags = 0; // :TODO: CODEFLAG_DEBUG; + code->header().main = 0; + code->header().code = sizeof(sp_file_code_t); + code->setBlob((uint8_t *)code_buffer.buffer(), code_buffer.length() * sizeof(cell)); - if (writeerror) - error(101,"disk full"); + // Set up the data section. Note pre-SourceMod 1.7, the |memsize| was + // computed as AMX::stp, which included the entire memory size needed to + // store the file. Here (in 1.7+), we allocate what is actually needed + // by the plugin. + data->header().datasize = data_buffer.length() * sizeof(cell); + data->header().memsize = + data->header().datasize + + glb_declared * sizeof(cell) + + pc_stksize * sizeof(cell); + data->header().data = sizeof(sp_file_data_t); + data->setBlob((uint8_t *)data_buffer.buffer(), data_buffer.length() * sizeof(cell)); - /* adjust the header */ - size=(int)hdr.cod; /* save, the value in the header may be swapped */ - #if BYTE_ORDER==BIG_ENDIAN - align32(&hdr.size); - align16(&hdr.magic); - align16(&hdr.flags); - align16(&hdr.defsize); - align32(&hdr.publics); - align32(&hdr.natives); - align32(&hdr.libraries); - align32(&hdr.pubvars); - align32(&hdr.tags); - align32(&hdr.nametable); - align32(&hdr.cod); - align32(&hdr.dat); - align32(&hdr.hea); - align32(&hdr.stp); - align32(&hdr.cip); - #endif - pc_resetbin(fout,0); - pc_writebin(fout,&hdr,sizeof hdr); + free(LabelTable); + LabelTable = nullptr; - /* return the size of the header (including name tables, but excluding code - * or data sections) - */ - return size; + // Add tables in the same order SourceMod 1.6 added them. + builder.add(code); + builder.add(data); + builder.add(publics); + builder.add(pubvars); + builder.add(natives); + builder.add(names); + builder.add(tags); + builder.write(buffer); +} + +static void splat_to_binary(const char *binfname, void *bytes, size_t size) +{ + // Note: error 161 will setjmp(), which skips destructors :( + FILE *fp = fopen(binfname, "wb"); + if (!fp) { + error(161, binfname); + return; + } + if (fwrite(bytes, 1, size, fp) != size) { + fclose(fp); + error(161, binfname); + return; + } + fclose(fp); +} + +void assemble(const char *binfname, void *fin) +{ + MemoryBuffer buffer; + assemble_to_buffer(&buffer, fin); + + // Buffer compression logic. + sp_file_hdr_t *header = (sp_file_hdr_t *)buffer.bytes(); + size_t region_size = header->imagesize - header->dataoffs; + size_t zbuf_max = compressBound(region_size); + Bytef *zbuf = (Bytef *)malloc(zbuf_max); + + uLong new_disksize = zbuf_max; + int err = compress2( + zbuf, + &new_disksize, + (Bytef *)(buffer.bytes() + header->dataoffs), + region_size, + Z_BEST_COMPRESSION + ); + if (err != Z_OK) { + free(zbuf); + pc_printf("Unable to compress, error %d\n", err); + pc_printf("Falling back to no compression.\n"); + splat_to_binary(binfname, buffer.bytes(), buffer.size()); + return; + } + + header->disksize = new_disksize + header->dataoffs; + header->compression = SmxConsts::FILE_COMPRESSION_GZ; + + buffer.rewind(header->dataoffs); + buffer.write(zbuf, new_disksize); + free(zbuf); + + 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) @@ -1163,17 +886,6 @@ static void append_dbginfo(void *fout) /* pass 2: generate the tables */ - #if BYTE_ORDER==BIG_ENDIAN - align32((uint32_t*)&dbghdr.size); - align16(&dbghdr.magic); - align16(&dbghdr.flags); - align16(&dbghdr.files); - align16(&dbghdr.lines); - align16(&dbghdr.symbols); - align16(&dbghdr.tags); - align16(&dbghdr.automatons); - align16(&dbghdr.states); - #endif writeerror |= !pc_writebin(fout,&dbghdr,sizeof dbghdr); /* file table */ @@ -1188,9 +900,6 @@ static void append_dbginfo(void *fout) if (codeidx!=previdx) { if (prevstr!=NULL) { assert(prevname!=NULL); - #if BYTE_ORDER==BIG_ENDIAN - aligncell(&previdx); - #endif writeerror |= !pc_writebin(fout,&previdx,sizeof previdx); writeerror |= !pc_writebin(fout,prevname,strlen(prevname)+1); } /* if */ @@ -1202,9 +911,6 @@ static void append_dbginfo(void *fout) } /* for */ if (prevstr!=NULL) { assert(prevname!=NULL); - #if BYTE_ORDER==BIG_ENDIAN - aligncell(&previdx); - #endif writeerror |= !pc_writebin(fout,&previdx,sizeof previdx); writeerror |= !pc_writebin(fout,prevname,strlen(prevname)+1); } /* if */ @@ -1217,10 +923,6 @@ static void append_dbginfo(void *fout) if (str[0]=='L') { dbgline.address=hex2long(str+2,&str); dbgline.line=(int32_t)hex2long(str,NULL); - #if BYTE_ORDER==BIG_ENDIAN - aligncell(&dbgline.address); - align32(&dbgline.line); - #endif writeerror |= !pc_writebin(fout,&dbgline,sizeof dbgline); } /* if */ } /* for */ @@ -1256,20 +958,9 @@ static void append_dbginfo(void *fout) } /* while */ } /* if */ dbgsymdim = dbgsym.dim; - #if BYTE_ORDER==BIG_ENDIAN - aligncell(&dbgsym.address); - align16(&dbgsym.tag); - aligncell(&dbgsym.codestart); - aligncell(&dbgsym.codeend); - align16(&dbgsym.dim); - #endif writeerror |= !pc_writebin(fout,&dbgsym,offsetof(AMX_DBG_SYMBOL, name)); writeerror |= !pc_writebin(fout,symname,strlen(symname)+1); for (dim=0; dimnext) { assert(strlen(constptr->name)>0); id1=(int16_t)(constptr->value & TAGMASK); - #if BYTE_ORDER==BIG_ENDIAN - align16(&id1); - #endif writeerror |= !pc_writebin(fout,&id1,sizeof id1); writeerror |= !pc_writebin(fout,constptr->name,strlen(constptr->name)+1); } /* for */ @@ -1291,10 +979,6 @@ static void append_dbginfo(void *fout) assert((constptr->index==0 && strlen(constptr->name)==0) || strlen(constptr->name)>0); id1=(int16_t)constptr->index; address=(ucell)constptr->value; - #if BYTE_ORDER==BIG_ENDIAN - align16(&id1); - aligncell(&address); - #endif writeerror |= !pc_writebin(fout,&id1,sizeof id1); writeerror |= !pc_writebin(fout,&address,sizeof address); writeerror |= !pc_writebin(fout,constptr->name,strlen(constptr->name)+1); @@ -1306,10 +990,6 @@ static void append_dbginfo(void *fout) id1=(int16_t)constptr->value; id2=(int16_t)constptr->index; address=(ucell)constptr->value; - #if BYTE_ORDER==BIG_ENDIAN - align16(&id1); - align16(&id2); - #endif writeerror |= !pc_writebin(fout,&id1,sizeof id1); writeerror |= !pc_writebin(fout,&id2,sizeof id2); writeerror |= !pc_writebin(fout,constptr->name,strlen(constptr->name)+1); diff --git a/sourcepawn/compiler/smx-builder.cpp b/sourcepawn/compiler/smx-builder.cpp new file mode 100644 index 00000000..d33f0330 --- /dev/null +++ b/sourcepawn/compiler/smx-builder.cpp @@ -0,0 +1,111 @@ +// vim: set sts=2 ts=8 sw=2 tw=99 et: +// +// Copyright (C) 2012-2014 David Anderson +// +// This file is part of SourcePawn. +// +// SourcePawn is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, either version 3 of the License, or (at your option) +// any later version. +// +// SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along with +// SourcePawn. If not, see http://www.gnu.org/licenses/. +#include "smx-builder.h" + +using namespace ke; +using namespace sp; + +SmxBuilder::SmxBuilder() +{ +} + +bool +SmxBuilder::write(ISmxBuffer *buf) +{ + sp_file_hdr_t header; + header.magic = SmxConsts::FILE_MAGIC; + header.version = SmxConsts::SP1_VERSION_1_1; + header.compression = SmxConsts::FILE_COMPRESSION_NONE; + + header.disksize = sizeof(header) + + sizeof(sp_file_section_t) * sections_.length(); + + // Note that |dataoffs| here is just to mimic what it would be in earlier + // versions of Pawn. Its value does not actually matter per the SMX spec, + // aside from having to be >= sizeof(sp_file_hdr_t). Here, we hint that + // only the region after the section list and names should be compressed. + header.dataoffs = header.disksize; + + size_t current_string_offset = 0; + for (size_t i = 0; i < sections_.length(); i++) { + Ref section = sections_[i]; + header.disksize += section->length(); + current_string_offset += section->name().length() + 1; + } + header.disksize += current_string_offset; + header.dataoffs += current_string_offset; + + header.imagesize = header.disksize; + header.sections = sections_.length(); + + // We put the string table after the sections table. + header.stringtab = sizeof(header) + sizeof(sp_file_section_t) * sections_.length(); + + if (!buf->write(&header, sizeof(header))) + return false; + + size_t current_offset = sizeof(header); + size_t current_data_offset = header.stringtab + current_string_offset; + current_string_offset = 0; + for (size_t i = 0; i < sections_.length(); i++) { + sp_file_section_t s; + s.nameoffs = current_string_offset; + s.dataoffs = current_data_offset; + s.size = sections_[i]->length(); + if (!buf->write(&s, sizeof(s))) + return false; + + current_offset += sizeof(s); + current_data_offset += s.size; + current_string_offset += sections_[i]->name().length() + 1; + } + assert(buf->pos() == current_offset); + assert(current_offset == header.stringtab); + + for (size_t i = 0; i < sections_.length(); i++) { + const AString &name = sections_[i]->name(); + if (!buf->write(name.chars(), name.length() + 1)) + return false; + } + current_offset += current_string_offset; + + assert(buf->pos() == current_offset); + + for (size_t i = 0; i < sections_.length(); i++) { + if (!sections_[i]->write(buf)) + return false; + current_offset += sections_[i]->length(); + } + + assert(buf->pos() == current_offset); + assert(current_offset == header.disksize); + + return true; +} + +bool +SmxNameTable::write(ISmxBuffer *buf) +{ + for (size_t i = 0; i < names_.length(); i++) { + Atom *str = names_[i]; + if (!buf->write(str->chars(), str->length() + 1)) + return false; + } + return true; +} + diff --git a/sourcepawn/compiler/smx-builder.h b/sourcepawn/compiler/smx-builder.h new file mode 100644 index 00000000..1b165c52 --- /dev/null +++ b/sourcepawn/compiler/smx-builder.h @@ -0,0 +1,180 @@ +// vim: set sts=2 ts=8 sw=2 tw=99 et: +// +// Copyright (C) 2012-2014 AlliedModders LLC, David Anderson +// +// This file is part of SourcePawn. +// +// SourcePawn is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, either version 3 of the License, or (at your option) +// any later version. +// +// SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along with +// SourcePawn. If not, see http://www.gnu.org/licenses/. +#ifndef _include_spcomp2_smx_builder_h_ +#define _include_spcomp2_smx_builder_h_ + +#include +#include +#include +#include +#include +#include "string-pool.h" + +namespace ke { + +class ISmxBuffer +{ + public: + virtual bool write(const void *bytes, size_t len) = 0; + virtual size_t pos() const = 0; +}; + +class SmxSection : public Refcounted +{ + public: + SmxSection(const char *name) + : name_(name) + { + } + + virtual bool write(ISmxBuffer *buf) = 0; + virtual size_t length() const = 0; + + const AString &name() const { + return name_; + } + + private: + AString name_; +}; + +template +class SmxBlobSection : public SmxSection +{ + public: + SmxBlobSection(const char *name) + : SmxSection(name) + { + } + + T &header() { + return t_; + } + void setBlob(uint8_t *blob, size_t len) { + extra_ = blob; + extra_len_ = len; + } + bool write(ISmxBuffer *buf) KE_OVERRIDE { + if (!buf->write(&t_, sizeof(t_))) + return false; + return buf->write(extra_, extra_len_); + } + size_t length() const KE_OVERRIDE { + return sizeof(t_) + extra_len_; + } + + private: + T t_; + uint8_t *extra_; + size_t extra_len_; +}; + +template +class SmxListSection : public SmxSection +{ + public: + SmxListSection(const char *name) + : SmxSection(name) + { + } + + void append(const T &t) { + list_.append(t); + } + T &add() { + list_.append(T()); + return list_.back(); + } + bool write(ISmxBuffer *buf) KE_OVERRIDE { + return buf->write(list_.buffer(), list_.length() * sizeof(T)); + } + size_t length() const KE_OVERRIDE { + return list_.length() * sizeof(T); + } + + private: + Vector list_; +}; + +class SmxNameTable : public SmxSection +{ + public: + SmxNameTable(const char *name) + : SmxSection(".names"), + buffer_size_(0) + { + name_table_.init(64); + } + + uint32_t add(StringPool &pool, const char *str) { + return add(pool.add(str)); + } + + uint32_t add(Atom *str) { + NameTable::Insert i = name_table_.findForAdd(str); + if (i.found()) + return i->value; + + assert(IsUint32AddSafe(buffer_size_, str->length() + 1)); + + uint32_t index = buffer_size_; + name_table_.add(i, str, index); + names_.append(str); + buffer_size_ += str->length() + 1; + return index; + } + + bool write(ISmxBuffer *buf) KE_OVERRIDE; + size_t length() const KE_OVERRIDE { + return buffer_size_; + } + + private: + struct HashPolicy { + static uint32_t hash(Atom *str) { + return HashPointer(str); + } + static bool matches(Atom *a, Atom *b) { + return a == b; + } + }; + typedef HashMap NameTable; + + NameTable name_table_; + Vector names_; + uint32_t buffer_size_; +}; + +class SmxBuilder +{ + public: + SmxBuilder(); + + bool write(ISmxBuffer *buf); + + void add(const Ref §ion) { + sections_.append(section); + } + + private: + Vector> sections_; +}; + +} // namespace ke + +#endif // _include_spcomp2_smx_builder_h_ diff --git a/sourcepawn/compiler/sp_file.cpp b/sourcepawn/compiler/sp_file.cpp deleted file mode 100644 index b849c311..00000000 --- a/sourcepawn/compiler/sp_file.cpp +++ /dev/null @@ -1,213 +0,0 @@ -#include -#include -#include -#include "sp_file.h" -#include "memfile.h" - -void *mf_open(const char *name); -void mf_close(void *handle); -size_t mf_write(const void *buf, size_t size, size_t count, void *handle); -size_t mf_read(void *buf, size_t size, size_t count, void *handle); -size_t mf_getpos(void *handle); -int mf_setpos(void *handle, size_t pos); - -sp_writefuncs_t cstd_funcs = -{ - mf_open, - mf_close, - mf_write, - mf_read, - mf_getpos, - mf_setpos -}; - -sp_file_t *spfw_create(const char *name, sp_writefuncs_t *optfuncs) -{ - sp_file_t file; - sp_file_t *pFile; - - if (!optfuncs) - optfuncs = &cstd_funcs; - - file.handle = optfuncs->fnOpen(name); - if (!file.handle) - return NULL; - - pFile = (sp_file_t *)malloc(sizeof(sp_file_t)); - - pFile->handle = file.handle; - memcpy(&pFile->funcs, optfuncs, sizeof(sp_writefuncs_t)); - pFile->curoffs = 0; - pFile->header.magic = SPFILE_MAGIC; - pFile->header.sections = 0; - pFile->header.stringtab = 0; - pFile->header.version = SPFILE_VERSION; - pFile->header.imagesize = 0; - pFile->header.disksize = 0; - pFile->header.compression = SPFILE_COMPRESSION_NONE; - pFile->header.dataoffs = 0; - pFile->lastsection = 0; - pFile->offsets = NULL; - pFile->sections = NULL; - pFile->state = -1; - pFile->nametab = NULL; - pFile->nametab_idx = 0; - - return pFile; -} - -void spfw_destroy(sp_file_t *spf) -{ - free(spf->sections); - free(spf->nametab); - free(spf->offsets); - spf->funcs.fnClose(spf->handle); - free(spf); -} - -uint8_t spfw_add_section(sp_file_t *spf, const char *name) -{ - size_t namelen; - uint8_t s; - if (spf->state != -1) - return 0; - - namelen = strlen(name) + 1; - - if (spf->header.sections == 0) - { - /** allocate for first section */ - spf->sections = (sp_file_section_t *)malloc(sizeof(sp_file_section_t)); - spf->offsets = (size_t *)malloc(sizeof(size_t)); - spf->nametab = (char *)malloc(namelen); - } else { - uint16_t num = spf->header.sections + 1; - spf->sections = (sp_file_section_t *)realloc(spf->sections, sizeof(sp_file_section_t) * num); - spf->offsets = (size_t *)realloc(spf->offsets, sizeof(size_t) * num); - spf->nametab = (char *)realloc(spf->nametab, spf->nametab_idx + namelen); - } - - s = spf->header.sections; - - spf->sections[s].nameoffs = spf->nametab_idx; - /** - * "fix" offset will be the second uint2 slot, which is after the previous sections after the header. - */ - spf->offsets[s] = sizeof(spf->header) + (sizeof(sp_file_section_t) * spf->header.sections) + sizeof(uint32_t); - strcpy(&spf->nametab[spf->nametab_idx], name); - spf->nametab_idx += namelen; - - return ++spf->header.sections; -} - -int spfw_finalize_header(sp_file_t *spf) -{ - uint32_t size; - if (spf->state != -1) - return -1; - - size = sizeof(sp_file_section_t) * spf->header.sections; - - spf->header.stringtab = sizeof(spf->header) + size; - spf->header.dataoffs = spf->header.stringtab + spf->nametab_idx; - if (spf->funcs.fnWrite(&spf->header, sizeof(spf->header), 1, spf->handle) != 1) - return -1; - if (spf->funcs.fnWrite(spf->sections, sizeof(sp_file_section_t), spf->header.sections, spf->handle) != - spf->header.sections) - return -1; - if (spf->funcs.fnWrite(spf->nametab, sizeof(char), spf->nametab_idx, spf->handle) != spf->nametab_idx) - return -1; - spf->curoffs = spf->funcs.fnGetPos(spf->handle); - spf->lastsection = spf->curoffs; - spf->state++; - - return 0; -} - -int spfw_next_section(sp_file_t *spf) -{ - uint8_t s; - uint32_t rest[2]; - - if (spf->state < 0 || spf->state > spf->header.sections) - return -1; - - if (spf->state == (int)spf->header.sections) - return 0; - - s = (uint8_t)spf->state; - - spf->curoffs = spf->funcs.fnGetPos(spf->handle); - spf->funcs.fnSetPos(spf->handle, spf->offsets[s]); - - rest[0] = spf->lastsection; - rest[1] = spf->curoffs - spf->lastsection; - if (spf->funcs.fnWrite(rest, sizeof(uint32_t), 2, spf->handle) != 2) - return -1; - - spf->funcs.fnSetPos(spf->handle, spf->curoffs); - spf->lastsection = spf->curoffs; - - spf->state++; - - return 1; -} - -int spfw_finalize_all(sp_file_t *spf) -{ - uint8_t offs; - - if (spf->state < spf->header.sections) - return -1; - - offs = offsetof(sp_file_hdr_t, imagesize); - spf->header.disksize = spf->funcs.fnGetPos(spf->handle); - spf->header.imagesize = spf->funcs.fnGetPos(spf->handle); - spf->funcs.fnSetPos(spf->handle, offs); - spf->funcs.fnWrite(&spf->header.imagesize, sizeof(uint32_t), 1, spf->handle); - spf->funcs.fnSetPos(spf->handle, spf->header.imagesize); - - return 1; -} - -/** - * More memory file operations - */ - -void *mf_open(const char *name) -{ - return memfile_creat(name, 1024); -} - -void mf_close(void *handle) -{ - memfile_destroy((memfile_t *)handle); -} - -size_t mf_write(const void *buf, size_t size, size_t count, void *handle) -{ - if (!count) - return 0; - - if (memfile_write((memfile_t *)handle, buf, size*count)) - return count; - - return 0; -} - -size_t mf_read(void *buf, size_t size, size_t count, void *handle) -{ - return memfile_read((memfile_t *)handle, buf, size*count) / count; -} - -size_t mf_getpos(void *handle) -{ - return (long)memfile_tell((memfile_t *)handle); -} - -int mf_setpos(void *handle, size_t pos) -{ - memfile_seek((memfile_t *)handle, (long)pos); - return 1; -} - diff --git a/sourcepawn/compiler/sp_file.h b/sourcepawn/compiler/sp_file.h deleted file mode 100644 index 2a9edcbe..00000000 --- a/sourcepawn/compiler/sp_file.h +++ /dev/null @@ -1,81 +0,0 @@ -#ifndef _INCLUDE_SPFILE_H -#define _INCLUDE_SPFILE_H - -#include "sp_file_headers.h" - -/** - * Used for overwriting writing routines. - */ -typedef struct sp_writefuncs_s -{ - void *(*fnOpen)(const char *); /* filename, returns handle */ - void (*fnClose)(void *); /* handle */ - /* buffer, size, count, handle, returns count written */ - size_t (*fnWrite)(const void *, size_t, size_t, void *); - /* buffer, size, count, handle, returns count read */ - size_t (*fnRead)(void *, size_t, size_t, void *); - /* returns current position from start */ - size_t (*fnGetPos)(void *); - /* sets current position from start, return 0 for success, nonzero for error */ - int (*fnSetPos)(void *, size_t); -} sp_writefuncs_t; - -typedef struct sp_file_s -{ - sp_file_hdr_t header; - sp_file_section_t *sections; - size_t *offsets; - sp_writefuncs_t funcs; - size_t lastsection; - size_t curoffs; - void *handle; - int state; - char *nametab; - size_t nametab_idx; -} sp_file_t; - -/** - * Creates a new SourcePawn binary file. - * You may optionally specify alternative writing functions. - */ -sp_file_t *spfw_create(const char *name, sp_writefuncs_t *optfuncs); - -/** - * Closes file handle and frees memory. - */ -void spfw_destroy(sp_file_t *spf); - -/** - * Adds a section name to the header. - * Only valid BEFORE finalization. - * Returns the number of sections, or 0 on failure. - */ -uint8_t spfw_add_section(sp_file_t *spf, const char *name); - -/** - * Finalizes the section header. - * This means no more sections can be added after this call. - * Also, aligns the writer to the first section. - * Returns 0 on success, nonzero on error. - */ -int spfw_finalize_header(sp_file_t *spf); - -/** - * Finalizes the current section and advances to the next. - * In order for this to be accurate, the file pointer must - * reside at the end before calling this, because the size - * is calculated by differencing with the last known offset. - * Returns 1 if there are more sections left, 0 otherwise. - * Returns -1 if the file state is wrong. - */ -int spfw_next_section(sp_file_t *spf); - -/** - * Finalizes all sections. - * Cannot be called until all sections are used. - * Must be called with the file pointer at the end. - * Also does compression! - */ -int spfw_finalize_all(sp_file_t *spf); - -#endif //_INCLUDE_SPFILE_H diff --git a/sourcepawn/compiler/sp_file_headers.h b/sourcepawn/compiler/sp_file_headers.h deleted file mode 100644 index 13d556fa..00000000 --- a/sourcepawn/compiler/sp_file_headers.h +++ /dev/null @@ -1,291 +0,0 @@ -/** - * vim: set ts=4 : - * ============================================================================= - * SourcePawn - * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. - * ============================================================================= - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License, version 3.0, as published by the - * Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - * - * As a special exception, AlliedModders LLC gives you permission to link the - * code of this program (as well as its derivative works) to "Half-Life 2," the - * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software - * by the Valve Corporation. You must obey the GNU General Public License in - * all respects for all other code used. Additionally, AlliedModders LLC grants - * this exception to all derivative works. AlliedModders LLC defines further - * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), - * or . - * - * Version: $Id$ - */ - -#ifndef _INCLUDE_SPFILE_HEADERS_H -#define _INCLUDE_SPFILE_HEADERS_H - -/** - * @file sp_file_headers.h - * @brief Defines the structure present in a SourcePawn compiled binary. - * - * Note: These structures should be 1-byte packed to match the file format. - */ - -#include -#include - -#define SPFILE_MAGIC 0x53504646 /**< Source Pawn File Format (SPFF) */ -#define SPFILE_VERSION 0x0102 /**< File format version */ - -//:TODO: better compiler/nix support -#if defined __GNUC__ - #pragma pack(1) /* structures must be packed (byte-aligned) */ -#else - #pragma pack(push) - #pragma pack(1) /* structures must be packed (byte-aligned) */ -#endif - -#define SPFILE_COMPRESSION_NONE 0 /**< No compression in file */ -#define SPFILE_COMPRESSION_GZ 1 /**< GZ compression */ - -/** - * @brief File section header format. - */ -typedef struct sp_file_section_s -{ - uint32_t nameoffs; /**< Relative offset into global string table */ - uint32_t dataoffs; /**< Offset into the data section of the file */ - uint32_t size; /**< Size of the section's entry in the data section */ -} sp_file_section_t; - -/** - * @brief File header format. If compression is 0, then disksize may be 0 - * to mean that only the imagesize is needed. - */ -typedef struct sp_file_hdr_s -{ - uint32_t magic; /**< Magic number */ - uint16_t version; /**< Version code */ - uint8_t compression;/**< Compression algorithm */ - uint32_t disksize; /**< Size on disk */ - uint32_t imagesize; /**< Size in memory */ - uint8_t sections; /**< Number of sections */ - uint32_t stringtab; /**< Offset to string table */ - uint32_t dataoffs; /**< Offset to file proper (any compression starts here) */ -} sp_file_hdr_t; - -#define SP_FLAG_DEBUG (1<<0) /**< Debug information is present in the file */ - -#define SP_CODEVERS_JIT1 9 /**< Code version for JIT1 */ -#define SP_CODEVERS_JIT2 10 /**< Code version for JIT2 */ - -/** - * @brief File-encoded format of the ".code" section. - */ -typedef struct sp_file_code_s -{ - uint32_t codesize; /**< Codesize in bytes */ - uint8_t cellsize; /**< Cellsize in bytes */ - uint8_t codeversion; /**< Version of opcodes supported */ - uint16_t flags; /**< Flags */ - uint32_t main; /**< Address to "main," if any */ - uint32_t code; /**< Relative offset to code */ -} sp_file_code_t; - -/** - * @brief File-encoded format of the ".data" section. - */ -typedef struct sp_file_data_s -{ - uint32_t datasize; /**< Size of data section in memory */ - uint32_t memsize; /**< Total mem required (includes data) */ - uint32_t data; /**< File offset to data (helper) */ -} sp_file_data_t; - -/** - * @brief File-encoded format of the ".publics" section. - */ -typedef struct sp_file_publics_s -{ - uint32_t address; /**< Address relative to code section */ - uint32_t name; /**< Index into nametable */ -} sp_file_publics_t; - -/** - * @brief File-encoded format of the ".natives" section. - */ -typedef struct sp_file_natives_s -{ - uint32_t name; /**< Index into nametable */ -} sp_file_natives_t; - -/** - * @brief File-encoded format of the ".pubvars" section. - */ -typedef struct sp_file_pubvars_s -{ - uint32_t address; /**< Address relative to the DAT section */ - uint32_t name; /**< Index into nametable */ -} sp_file_pubvars_t; - -/** - * @brief File-encoded tag info. - */ -typedef struct sp_file_tag_s -{ - uint32_t tag_id; /**< Tag ID from compiler */ - uint32_t name; /**< Index into nametable */ -} sp_file_tag_t; - -/** - * @brief File-encoded debug information table. - */ -typedef struct sp_fdbg_info_s -{ - uint32_t num_files; /**< number of files */ - uint32_t num_lines; /**< number of lines */ - uint32_t num_syms; /**< number of symbols */ - uint32_t num_arrays; /**< number of symbols which are arrays */ -} sp_fdbg_info_t; - -/** - * @brief File-encoded debug file table. - */ -typedef struct sp_fdbg_file_s -{ - uint32_t addr; /**< Address into code */ - uint32_t name; /**< Offset into debug nametable */ -} sp_fdbg_file_t; - -/** - * @brief File-encoded debug line table. - */ -typedef struct sp_fdbg_line_s -{ - uint32_t addr; /**< Address into code */ - uint32_t line; /**< Line number */ -} sp_fdbg_line_t; - -#define SP_SYM_VARIABLE 1 /**< Cell that has an address and that can be fetched directly (lvalue) */ -#define SP_SYM_REFERENCE 2 /**< VARIABLE, but must be dereferenced */ -#define SP_SYM_ARRAY 3 /**< Symbol is an array */ -#define SP_SYM_REFARRAY 4 /**< An array passed by reference (i.e. a pointer) */ -#define SP_SYM_FUNCTION 9 /**< Symbol is a function */ -#define SP_SYM_VARARGS 11 /**< Variadic argument start. */ - -/** - * @brief File-encoded debug symbol information. - */ -typedef struct sp_fdbg_symbol_s -{ - int32_t addr; /**< Address rel to DAT or stack frame */ - int16_t tagid; /**< Tag id */ - uint32_t codestart; /**< Start scope validity in code */ - uint32_t codeend; /**< End scope validity in code */ - uint8_t ident; /**< Variable type */ - uint8_t vclass; /**< Scope class (local vs global) */ - uint16_t dimcount; /**< Dimension count (for arrays) */ - uint32_t name; /**< Offset into debug nametable */ -} sp_fdbg_symbol_t; - -/** - * @brief File-encoded debug symbol array dimension info. - */ -typedef struct sp_fdbg_arraydim_s -{ - int16_t tagid; /**< Tag id */ - uint32_t size; /**< Size of dimension */ -} sp_fdbg_arraydim_t; - -/** Typedef for .names table */ -typedef char * sp_file_nametab_t; - -/** - * @brief File encoding for the dbg.natives table. - * - * This header is followed by variable length entries of sp_fdbg_native. - */ -typedef struct sp_fdbg_ntvtab_s -{ - uint32_t num_entries; /**< Number of entries. */ -} sp_fdbg_ntvtab_t; - -#define SP_NTVDBG_VARARGS (1<<0) /**< Formal args are followed by '...' */ - -/** - * @brief File encoding of native debug info. - * - * Each entry is followed by an sp_fdbg_ntvarg_t for each narg. - */ -typedef struct sp_fdbg_native_s -{ - uint32_t index; /**< Native index in the plugin. */ - uint32_t name; /**< Offset into debug nametable. */ - int16_t tagid; /**< Return tag. */ - uint16_t nargs; /**< Number of formal arguments. */ -} sp_fdbg_native_t; - -/** - * @brief File encoding of native arguments. - * - * Each entry is followed by an sp_fdbg_arraydim_t for each dimcount. - */ -typedef struct fp_fdbg_ntvarg_s -{ - uint8_t ident; /**< Variable type */ - int16_t tagid; /**< Tag id */ - uint16_t dimcount; /**< Dimension count (for arrays) */ - uint32_t name; /**< Offset into debug nametable */ -} sp_fdbg_ntvarg_t; - -#if defined __GNUC__ -#pragma pack() /* reset default packing */ -#else -#pragma pack(pop) /* reset previous packing */ -#endif - -/** - * Okay, my mistake here. I apologize. I changed the packing by accident and there is no - * version bump aside from the presence of the native debug table. Cat's out of the bag - * for SourceMod and we have no choice but to shim compat for the old version. For people - * parsing plugins on their own, use the presence of the native debug table to decide this. - * If there are no natives (really, there is a very very low chance of this), heuristics - * might be necessary. I've bumped the version to 0x0102 but there may have been plugins - * in the 0x0101 window with no natives. - */ - -/** - * @brief Unpacked file-encoded debug symbol array dimension info. - */ -typedef struct sp_u_fdbg_arraydim_s -{ - int16_t tagid; /**< Tag id */ - uint32_t size; /**< Size of dimension */ -} sp_u_fdbg_arraydim_t; - -/** - * @brief Unpacked file-encoded debug symbol information. - */ -typedef struct sp_u_fdbg_symbol_s -{ - int32_t addr; /**< Address rel to DAT or stack frame */ - int16_t tagid; /**< Tag id */ - uint32_t codestart; /**< Start scope validity in code */ - uint32_t codeend; /**< End scope validity in code */ - uint8_t ident; /**< Variable type */ - uint8_t vclass; /**< Scope class (local vs global) */ - uint16_t dimcount; /**< Dimension count (for arrays) */ - uint32_t name; /**< Offset into debug nametable */ -} sp_u_fdbg_symbol_t; - - -#endif //_INCLUDE_SPFILE_HEADERS_H - diff --git a/sourcepawn/compiler/sp_symhash.cpp b/sourcepawn/compiler/sp_symhash.cpp index b843698c..1b50b0ae 100644 --- a/sourcepawn/compiler/sp_symhash.cpp +++ b/sourcepawn/compiler/sp_symhash.cpp @@ -2,7 +2,6 @@ #include #include #include -#include "sp_file_headers.h" #include "sc.h" #include "sp_symhash.h" #include diff --git a/sourcepawn/compiler/string-pool.h b/sourcepawn/compiler/string-pool.h new file mode 100644 index 00000000..94b21655 --- /dev/null +++ b/sourcepawn/compiler/string-pool.h @@ -0,0 +1,139 @@ +/* vim: set ts=2 sw=2 tw=99 et: + * + * Copyright (C) 2012-2014 AlliedModders LLC, David Anderson + * + * This file is part of SourcePawn. + * + * SourcePawn is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * SourcePawn. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_jitcraft_string_pool_h_ +#define _include_jitcraft_string_pool_h_ + +#include +#include +#include + +namespace ke { + +// Wrapper around AString, since we might want to remove charfs() in a GC-safe +// implementation. +class Atom +{ + friend class StringPool; + + private: + Atom(const char *str, size_t len) + : str_(str, len) + { + } + + public: + size_t length() const { + return str_.length(); + } + const char *chars() const { + return str_.chars(); + } + + private: + AString str_; +}; + +class CharsAndLength +{ + public: + CharsAndLength() + : str_(nullptr), + length_(0) + { + } + + CharsAndLength(const char *str, size_t length) + : str_(str), + length_(length) + { + } + + const char *str() const { + return str_; + } + size_t length() const { + return length_; + } + + private: + const char *str_; + size_t length_; +}; + +class StringPool +{ + public: + StringPool() + : table_(SystemAllocatorPolicy()) + { + init(); + } + + ~StringPool() + { + if (!table_.elements()) + return; + for (Table::iterator i(&table_); !i.empty(); i.next()) + delete *i; + } + + bool init() { + return table_.init(256); + } + + Atom *add(const char *str, size_t length) { + CharsAndLength chars(str, length); + Table::Insert p = table_.findForAdd(chars); + if (!p.found() && !table_.add(p, new Atom(str, length))) + return nullptr; + return *p; + } + + Atom *add(const char *str) { + return add(str, strlen(str)); + } + + private: + struct Policy { + typedef Atom *Payload; + + static uint32_t hash(const char *key) { + return HashCharSequence(key, strlen(key)); + } + + static uint32_t hash(const CharsAndLength &key) { + return HashCharSequence(key.str(), key.length()); + } + + static bool matches(const CharsAndLength &key, const Payload &e) { + if (key.length() != e->length()) + return false; + return strcmp(key.str(), e->chars()) == 0; + } + }; + + typedef HashTable Table; + + private: + Table table_; +}; + +} // namespace ke + +#endif // _include_jitcraft_string_pool_h_ diff --git a/sourcepawn/include/smx/smx-headers.h b/sourcepawn/include/smx/smx-headers.h index dc9d7dcf..325f16c8 100644 --- a/sourcepawn/include/smx/smx-headers.h +++ b/sourcepawn/include/smx/smx-headers.h @@ -39,9 +39,8 @@ struct SmxConsts static const uint32_t FILE_MAGIC = 0x53504646; // File format verison number. - // 0x0101 - Initial version used by spcomp1 and SourceMod 1.0. - // 0x0102 - Used by spcomp1 and SourceMod 1.1+. - // 0x0103 - Used by SourceMod 1.7+. + // 0x0101 - SourcePawn 1.0; initial version used by SourceMod 1.0. + // 0x0102 - SourcePawn 1.1; used by SourceMod 1.1+. // 0x0200 - Used by spcomp2. // // The major version bits (8-15) indicate a product number. Consumers should @@ -49,8 +48,10 @@ struct SmxConsts // // The minor version bits (0-7) indicate a compatibility revision. Any minor // version higher than the current version should be rejected. - static const uint16_t SP1_VERSION_MIN = 0x0101; - static const uint16_t SP1_VERSION_MAX = 0x0103; + static const uint16_t SP1_VERSION_1_0 = 0x0101; + static const uint16_t SP1_VERSION_1_1 = 0x0102; + static const uint16_t SP1_VERSION_MIN = SP1_VERSION_1_0; + static const uint16_t SP1_VERSION_MAX = SP1_VERSION_1_1; static const uint16_t SP2_VERSION_MIN = 0x0200; static const uint16_t SP2_VERSION_MAX = 0x0200; @@ -94,18 +95,28 @@ typedef struct sp_file_hdr_s uint32_t magic; uint16_t version; - // Compression algorithm. If the file is not compressed, then imagesize and - // disksize are the same value, and dataoffs is 0. + // disksize, imagesize, and dataoffs (at the end) describe a region of the + // file that may be compressed. The region must occur after the initial + // sp_file_hdr_t header. For SourceMod compatibility, the meanings are as + // follows. // - // The start of the compressed region is indicated by dataoffs. The length - // of the compressed region is (disksize - dataoffs). The amount of memory - // required to hold the decompressed bytes is (imagesize - dataoffs). The - // compressed region should be expanded in-place. That is, bytes before - // "dataoffs" should be retained, and the decompressed region should be - // appended. + // Without compression: + // imagesize is the amount of bytes that must be read into memory to parse + // the SMX container, starting from the first byte of the file. + // disksize is undefined. + // dataoffs is undefined. // - // |imagesize| is the amount of memory required to hold the entire container - // in memory. + // With compression: + // dataoffs is an offset to the start of the compression region. + // disksize is the length of the compressed region, in bytes, plus dataoffs. + // imagesize is the size of the entire SMX container after decompression. + // + // This means that at least |imagesize-dataoffs| must be allocated to + // decompress, and the compressed region's length is |datasize-dataoffs|. + // + // The compressed region should always be expanded "in-place". That is, to + // parse the container, the compressed bytes should be replaced with + // decompressed bytes. // // Note: This scheme may seem odd. It's a combination of historical debt and // previously unspecified behavior. The original .amx file format contained diff --git a/sourcepawn/jit/Makefile.shell b/sourcepawn/jit/Makefile.shell index ad2fd0ae..127f0aaa 100644 --- a/sourcepawn/jit/Makefile.shell +++ b/sourcepawn/jit/Makefile.shell @@ -53,7 +53,7 @@ LINK = -m32 -lm -lpthread -lrt INCLUDE = -I. -I.. -I$(SMSDK)/public -I$(SMSDK)/public/jit -I$(SMSDK)/public/jit/x86 \ -I$(SMSDK)/public/sourcepawn -I$(MMSOURCE17)/core/sourcehook -I$(SMSDK)/knight/shared -Ix86 \ - -I$(SMSDK)/public/amtl + -I$(SMSDK)/public/amtl -I$(SMSDK)/sourcepawn/include CFLAGS += -D_LINUX -Dstricmp=strcasecmp -D_stricmp=strcasecmp -D_strnicmp=strncasecmp -Dstrnicmp=strncasecmp \ -D_snprintf=snprintf -D_vsnprintf=vsnprintf -D_alloca=alloca -Dstrcmpi=strcasecmp -Wall -DHAVE_STDINT_H \