mirror of
https://github.com/xomboverlord/ldc.git
synced 2026-01-11 18:33:14 +01:00
In many parts the DMD frontend assumes a little endian CPU. In some parts there are checks for endianess but they are incomplete and the used definition is wrong. (Test for endianess will be removed in dmd 2.062.) In this commit I add the required #if's and also add a CMake test for endianess because there is no single compiler definition to check for.
1400 lines
37 KiB
C
1400 lines
37 KiB
C
|
|
// Compiler implementation of the D programming language
|
|
// Copyright (c) 1999-2012 by Digital Mars
|
|
// All Rights Reserved
|
|
// written by Walter Bright
|
|
// http://www.digitalmars.com
|
|
// License for redistribution is by either the Artistic License
|
|
// in artistic.txt, or the GNU General Public License in gnu.txt.
|
|
// See the included readme.txt for details.
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <assert.h>
|
|
|
|
#if (defined (__SVR4) && defined (__sun))
|
|
#include <alloca.h>
|
|
#endif
|
|
|
|
#if defined(_MSC_VER) || defined(__MINGW32__)
|
|
#include <malloc.h>
|
|
#endif
|
|
|
|
#ifdef IN_GCC
|
|
#include "gdc_alloca.h"
|
|
#endif
|
|
|
|
#include "rmem.h"
|
|
|
|
#include "mars.h"
|
|
#include "module.h"
|
|
#include "parse.h"
|
|
#include "scope.h"
|
|
#include "identifier.h"
|
|
#include "id.h"
|
|
#include "import.h"
|
|
#include "dsymbol.h"
|
|
#include "hdrgen.h"
|
|
#include "lexer.h"
|
|
|
|
// stricmp
|
|
#if __GNUC__ && !_WIN32
|
|
#include "gnuc.h"
|
|
#endif
|
|
|
|
#ifdef IN_GCC
|
|
#include "d-dmd-gcc.h"
|
|
#endif
|
|
|
|
#if IN_LLVM
|
|
#if LDC_LLVM_VER >= 303
|
|
#include "llvm/IR/Type.h"
|
|
#include "llvm/IR/LLVMContext.h"
|
|
#include "llvm/IR/DerivedTypes.h"
|
|
#else
|
|
#include "llvm/Type.h"
|
|
#include "llvm/LLVMContext.h"
|
|
#include "llvm/DerivedTypes.h"
|
|
#endif
|
|
#include "llvm/Support/CommandLine.h"
|
|
#include <map>
|
|
|
|
static llvm::cl::opt<bool> preservePaths("op",
|
|
llvm::cl::desc("Do not strip paths from source file"),
|
|
llvm::cl::ZeroOrMore);
|
|
|
|
static llvm::cl::opt<bool> fqnNames("oq",
|
|
llvm::cl::desc("Write object files with fully qualified names"),
|
|
llvm::cl::ZeroOrMore);
|
|
#endif
|
|
|
|
AggregateDeclaration *Module::moduleinfo;
|
|
|
|
Module *Module::rootModule;
|
|
DsymbolTable *Module::modules;
|
|
Modules Module::amodules;
|
|
|
|
Dsymbols Module::deferred; // deferred Dsymbol's needing semantic() run on them
|
|
unsigned Module::dprogress;
|
|
|
|
void Module::init()
|
|
{
|
|
modules = new DsymbolTable();
|
|
}
|
|
|
|
Module::Module(char *filename, Identifier *ident, int doDocComment, int doHdrGen)
|
|
: Package(ident)
|
|
{
|
|
FileName *srcfilename;
|
|
#if IN_DMD
|
|
FileName *objfilename;
|
|
FileName *symfilename;
|
|
#endif
|
|
|
|
// printf("Module::Module(filename = '%s', ident = '%s')\n", filename, ident->toChars());
|
|
this->arg = filename;
|
|
md = NULL;
|
|
errors = 0;
|
|
numlines = 0;
|
|
members = NULL;
|
|
isDocFile = 0;
|
|
needmoduleinfo = 0;
|
|
#ifdef IN_GCC
|
|
strictlyneedmoduleinfo = 0;
|
|
#endif
|
|
selfimports = 0;
|
|
insearch = 0;
|
|
searchCacheIdent = NULL;
|
|
searchCacheSymbol = NULL;
|
|
searchCacheFlags = 0;
|
|
semanticstarted = 0;
|
|
semanticRun = 0;
|
|
decldefs = NULL;
|
|
vmoduleinfo = NULL;
|
|
#if IN_DMD
|
|
massert = NULL;
|
|
munittest = NULL;
|
|
marray = NULL;
|
|
sictor = NULL;
|
|
sctor = NULL;
|
|
sdtor = NULL;
|
|
ssharedctor = NULL;
|
|
sshareddtor = NULL;
|
|
stest = NULL;
|
|
sfilename = NULL;
|
|
#endif
|
|
root = 0;
|
|
importedFrom = NULL;
|
|
srcfile = NULL;
|
|
objfile = NULL;
|
|
docfile = NULL;
|
|
hdrfile = NULL;
|
|
|
|
debuglevel = 0;
|
|
debugids = NULL;
|
|
debugidsNot = NULL;
|
|
versionlevel = 0;
|
|
versionids = NULL;
|
|
versionidsNot = NULL;
|
|
|
|
macrotable = NULL;
|
|
escapetable = NULL;
|
|
safe = FALSE;
|
|
#if IN-DMD
|
|
doppelganger = 0;
|
|
cov = NULL;
|
|
covb = NULL;
|
|
#endif
|
|
|
|
nameoffset = 0;
|
|
namelen = 0;
|
|
|
|
srcfilename = FileName::defaultExt(filename, global.mars_ext);
|
|
if (!srcfilename->equalsExt(global.mars_ext) &&
|
|
!srcfilename->equalsExt(global.hdr_ext) &&
|
|
!srcfilename->equalsExt("dd"))
|
|
{
|
|
error("source file name '%s' must have .%s extension", srcfilename->toChars(), global.mars_ext);
|
|
fatal();
|
|
}
|
|
#if !IN_LLVM
|
|
char *argobj;
|
|
if (global.params.objname)
|
|
argobj = global.params.objname;
|
|
#if 0
|
|
else if (global.params.preservePaths)
|
|
argobj = filename;
|
|
else
|
|
argobj = FileName::name(filename);
|
|
if (!FileName::absolute(argobj))
|
|
{
|
|
argobj = FileName::combine(global.params.objdir, argobj);
|
|
}
|
|
#else // Bugzilla 3547
|
|
else
|
|
{
|
|
if (global.params.preservePaths)
|
|
argobj = filename;
|
|
else
|
|
argobj = FileName::name(filename);
|
|
if (!FileName::absolute(argobj))
|
|
{
|
|
argobj = FileName::combine(global.params.objdir, argobj);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (global.params.objname)
|
|
objfilename = new FileName(argobj, 0);
|
|
else
|
|
objfilename = FileName::forceExt(argobj, global.obj_ext);
|
|
|
|
symfilename = FileName::forceExt(filename, global.sym_ext);
|
|
#endif
|
|
|
|
srcfile = new File(srcfilename);
|
|
#if IN_DMD
|
|
if (doDocComment)
|
|
{
|
|
setDocfile();
|
|
}
|
|
|
|
if (doHdrGen)
|
|
{
|
|
setHdrfile();
|
|
}
|
|
|
|
objfile = new File(objfilename);
|
|
symfile = new File(symfilename);
|
|
#endif
|
|
#if IN_LLVM
|
|
// LDC
|
|
llvmForceLogging = false;
|
|
moduleInfoVar = NULL;
|
|
moduleInfoType = llvm::StructType::create(llvm::getGlobalContext());
|
|
this->doDocComment = doDocComment;
|
|
this->doHdrGen = doHdrGen;
|
|
this->isRoot = false;
|
|
this->arrayfuncs = 0;
|
|
#endif
|
|
}
|
|
#if IN_LLVM
|
|
File* Module::buildFilePath(const char* forcename, const char* path, const char* ext)
|
|
{
|
|
const char *argobj;
|
|
if (forcename)
|
|
argobj = forcename;
|
|
else
|
|
{
|
|
if (preservePaths)
|
|
argobj = (char*)this->arg;
|
|
else
|
|
argobj = FileName::name((char*)this->arg);
|
|
|
|
if (fqnNames)
|
|
{
|
|
if(md)
|
|
argobj = FileName::replaceName((char*)argobj, md->toChars());
|
|
else
|
|
argobj = FileName::replaceName((char*)argobj, toChars());
|
|
|
|
// add ext, otherwise forceExt will make nested.module into nested.bc
|
|
size_t len = strlen(argobj);
|
|
size_t extlen = strlen(ext);
|
|
char* s = (char *)alloca(len + 1 + extlen + 1);
|
|
memcpy(s, argobj, len);
|
|
s[len] = '.';
|
|
memcpy(s + len + 1, ext, extlen + 1);
|
|
s[len+1+extlen] = 0;
|
|
argobj = s;
|
|
}
|
|
}
|
|
|
|
if (!FileName::absolute(argobj))
|
|
{
|
|
argobj = FileName::combine(path, argobj);
|
|
}
|
|
|
|
FileName::ensurePathExists(FileName::path(argobj));
|
|
|
|
// always append the extension! otherwise hard to make output switches consistent
|
|
// if (forcename)
|
|
// return new File(argobj);
|
|
// else
|
|
// allow for .o and .obj on windows
|
|
#if _WIN32
|
|
if (ext == global.params.objdir && FileName::ext(argobj)
|
|
&& stricmp(FileName::ext(argobj), global.obj_ext_alt) == 0)
|
|
return new File((char*)argobj);
|
|
#endif
|
|
return new File(FileName::forceExt(argobj, ext));
|
|
}
|
|
#endif
|
|
#if IN_DMD
|
|
void Module::setDocfile()
|
|
{
|
|
FileName *docfilename;
|
|
char *argdoc;
|
|
|
|
if (global.params.docname)
|
|
argdoc = global.params.docname;
|
|
else if (global.params.preservePaths)
|
|
argdoc = (char *)arg;
|
|
else
|
|
argdoc = FileName::name((char *)arg);
|
|
if (!FileName::absolute(argdoc))
|
|
{ //FileName::ensurePathExists(global.params.docdir);
|
|
argdoc = FileName::combine(global.params.docdir, argdoc);
|
|
}
|
|
if (global.params.docname)
|
|
docfilename = new FileName(argdoc, 0);
|
|
else
|
|
docfilename = FileName::forceExt(argdoc, global.doc_ext);
|
|
|
|
if (docfilename->equals(srcfile->name))
|
|
{ error("Source file and documentation file have same name '%s'", srcfile->name->str);
|
|
fatal();
|
|
}
|
|
|
|
docfile = new File(docfilename);
|
|
}
|
|
|
|
void Module::setHdrfile()
|
|
{
|
|
FileName *hdrfilename;
|
|
char *arghdr;
|
|
|
|
if (global.params.hdrname)
|
|
arghdr = global.params.hdrname;
|
|
else if (global.params.preservePaths)
|
|
arghdr = (char *)arg;
|
|
else
|
|
arghdr = FileName::name((char *)arg);
|
|
if (!FileName::absolute(arghdr))
|
|
{ //FileName::ensurePathExists(global.params.hdrdir);
|
|
arghdr = FileName::combine(global.params.hdrdir, arghdr);
|
|
}
|
|
if (global.params.hdrname)
|
|
hdrfilename = new FileName(arghdr, 0);
|
|
else
|
|
hdrfilename = FileName::forceExt(arghdr, global.hdr_ext);
|
|
|
|
if (hdrfilename->equals(srcfile->name))
|
|
{ error("Source file and 'header' file have same name '%s'", srcfile->name->str);
|
|
fatal();
|
|
}
|
|
|
|
hdrfile = new File(hdrfilename);
|
|
}
|
|
#endif
|
|
|
|
#if IN_LLVM
|
|
|
|
// LDC
|
|
static void check_and_add_output_file(Module* NewMod, const std::string& str)
|
|
{
|
|
typedef std::map<std::string, Module*> map_t;
|
|
static map_t files;
|
|
|
|
map_t::iterator i = files.find(str);
|
|
if (i != files.end())
|
|
{
|
|
Module* ThisMod = i->second;
|
|
error(Loc(), "Output file '%s' for module '%s' collides with previous module '%s'. See the -oq option",
|
|
str.c_str(), NewMod->toPrettyChars(), ThisMod->toPrettyChars());
|
|
fatal();
|
|
}
|
|
files.insert(std::make_pair(str, NewMod));
|
|
}
|
|
|
|
void Module::buildTargetFiles(bool singleObj)
|
|
{
|
|
if(objfile &&
|
|
(!doDocComment || docfile) &&
|
|
(!doHdrGen || hdrfile))
|
|
return;
|
|
|
|
if(!objfile)
|
|
{
|
|
if (global.params.output_o)
|
|
objfile = Module::buildFilePath(global.params.objname, global.params.objdir, global.params.os == OSWindows ? global.obj_ext_alt : global.obj_ext);
|
|
else if (global.params.output_bc)
|
|
objfile = Module::buildFilePath(global.params.objname, global.params.objdir, global.bc_ext);
|
|
else if (global.params.output_ll)
|
|
objfile = Module::buildFilePath(global.params.objname, global.params.objdir, global.ll_ext);
|
|
else if (global.params.output_s)
|
|
objfile = Module::buildFilePath(global.params.objname, global.params.objdir, global.s_ext);
|
|
}
|
|
if(doDocComment && !docfile)
|
|
docfile = Module::buildFilePath(global.params.docname, global.params.docdir, global.doc_ext);
|
|
if(doHdrGen && !hdrfile)
|
|
hdrfile = Module::buildFilePath(global.params.hdrname, global.params.hdrdir, global.hdr_ext);
|
|
|
|
// safety check: never allow obj, doc or hdr file to have the source file's name
|
|
if(stricmp(FileName::name(objfile->name->str), FileName::name((char*)this->arg)) == 0)
|
|
{
|
|
error("Output object files with the same name as the source file are forbidden");
|
|
fatal();
|
|
}
|
|
if(docfile && stricmp(FileName::name(docfile->name->str), FileName::name((char*)this->arg)) == 0)
|
|
{
|
|
error("Output doc files with the same name as the source file are forbidden");
|
|
fatal();
|
|
}
|
|
if(hdrfile && stricmp(FileName::name(hdrfile->name->str), FileName::name((char*)this->arg)) == 0)
|
|
{
|
|
error("Output header files with the same name as the source file are forbidden");
|
|
fatal();
|
|
}
|
|
|
|
// LDC
|
|
// another safety check to make sure we don't overwrite previous output files
|
|
if (!singleObj)
|
|
check_and_add_output_file(this, objfile->name->str);
|
|
if (docfile)
|
|
check_and_add_output_file(this, docfile->name->str);
|
|
if (hdrfile)
|
|
check_and_add_output_file(this, hdrfile->name->str);
|
|
}
|
|
|
|
#endif
|
|
|
|
void Module::deleteObjFile()
|
|
{
|
|
if (global.params.obj)
|
|
objfile->remove();
|
|
//if (global.params.llvmBC)
|
|
//bcfile->remove();
|
|
if (doDocComment && docfile)
|
|
docfile->remove();
|
|
}
|
|
|
|
Module::~Module()
|
|
{
|
|
#if IN_LLVM
|
|
delete moduleInfoType;
|
|
#endif
|
|
}
|
|
|
|
const char *Module::kind()
|
|
{
|
|
return "module";
|
|
}
|
|
|
|
Module *Module::load(Loc loc, Identifiers *packages, Identifier *ident)
|
|
{ Module *m;
|
|
char *filename;
|
|
|
|
//printf("Module::load(ident = '%s')\n", ident->toChars());
|
|
|
|
// Build module filename by turning:
|
|
// foo.bar.baz
|
|
// into:
|
|
// foo\bar\baz
|
|
filename = ident->toChars();
|
|
if (packages && packages->dim)
|
|
{
|
|
OutBuffer buf;
|
|
|
|
for (size_t i = 0; i < packages->dim; i++)
|
|
{ Identifier *pid = (*packages)[i];
|
|
|
|
buf.writestring(pid->toChars());
|
|
#if _WIN32
|
|
buf.writeByte('\\');
|
|
#else
|
|
buf.writeByte('/');
|
|
#endif
|
|
}
|
|
buf.writestring(filename);
|
|
buf.writeByte(0);
|
|
filename = (char *)buf.extractData();
|
|
}
|
|
|
|
m = new Module(filename, ident, 0, 0);
|
|
m->loc = loc;
|
|
|
|
/* Search along global.path for .di file, then .d file.
|
|
*/
|
|
char *result = NULL;
|
|
FileName *fdi = FileName::forceExt(filename, global.hdr_ext);
|
|
FileName *fd = FileName::forceExt(filename, global.mars_ext);
|
|
char *sdi = fdi->toChars();
|
|
char *sd = fd->toChars();
|
|
|
|
if (FileName::exists(sdi))
|
|
result = sdi;
|
|
else if (FileName::exists(sd))
|
|
result = sd;
|
|
else if (FileName::absolute(filename))
|
|
;
|
|
else if (!global.path)
|
|
;
|
|
else
|
|
{
|
|
for (size_t i = 0; i < global.path->dim; i++)
|
|
{
|
|
char *p = (*global.path)[i];
|
|
char *n = FileName::combine(p, sdi);
|
|
if (FileName::exists(n))
|
|
{ result = n;
|
|
break;
|
|
}
|
|
mem.free(n);
|
|
n = FileName::combine(p, sd);
|
|
if (FileName::exists(n))
|
|
{ result = n;
|
|
break;
|
|
}
|
|
mem.free(n);
|
|
}
|
|
}
|
|
if (result)
|
|
m->srcfile = new File(result);
|
|
|
|
if (global.params.verbose)
|
|
{
|
|
printf("import ");
|
|
if (packages)
|
|
{
|
|
for (size_t i = 0; i < packages->dim; i++)
|
|
{ Identifier *pid = (*packages)[i];
|
|
printf("%s.", pid->toChars());
|
|
}
|
|
}
|
|
printf("%s\t(%s)\n", ident->toChars(), m->srcfile->toChars());
|
|
}
|
|
|
|
if (!m->read(loc))
|
|
return NULL;
|
|
|
|
m->parse();
|
|
|
|
#ifdef IN_GCC
|
|
d_gcc_magic_module(m);
|
|
#endif
|
|
|
|
return m;
|
|
}
|
|
|
|
bool Module::read(Loc loc)
|
|
{
|
|
//printf("Module::read('%s') file '%s'\n", toChars(), srcfile->toChars());
|
|
if (srcfile->read())
|
|
{ error(loc, "is in file '%s' which cannot be read", srcfile->toChars());
|
|
if (!global.gag)
|
|
{ /* Print path
|
|
*/
|
|
if (global.path)
|
|
{
|
|
for (size_t i = 0; i < global.path->dim; i++)
|
|
{
|
|
char *p = (*global.path)[i];
|
|
fprintf(stdmsg, "import path[%llu] = %s\n", (ulonglong)i, p);
|
|
}
|
|
}
|
|
else
|
|
fprintf(stdmsg, "Specify path to file '%s' with -I switch\n", srcfile->toChars());
|
|
fatal();
|
|
}
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
inline unsigned readwordLE(unsigned short *p)
|
|
{
|
|
#if IN_LLVM
|
|
#if __LITTLE_ENDIAN__
|
|
return *p;
|
|
#else
|
|
return (((unsigned char *)p)[1] << 8) | ((unsigned char *)p)[0];
|
|
#endif
|
|
#else
|
|
#if LITTLE_ENDIAN
|
|
return *p;
|
|
#else
|
|
return (((unsigned char *)p)[1] << 8) | ((unsigned char *)p)[0];
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
inline unsigned readwordBE(unsigned short *p)
|
|
{
|
|
#if IN_LLVM
|
|
#if __BIG_ENDIAN__
|
|
return *p;
|
|
#else
|
|
return (((unsigned char *)p)[0] << 8) | ((unsigned char *)p)[1];
|
|
#endif
|
|
#else
|
|
return (((unsigned char *)p)[0] << 8) | ((unsigned char *)p)[1];
|
|
#endif
|
|
}
|
|
|
|
inline unsigned readlongLE(unsigned *p)
|
|
{
|
|
#if IN_LLVM
|
|
#if __LITTLE_ENDIAN__
|
|
return *p;
|
|
#else
|
|
return ((unsigned char *)p)[0] |
|
|
(((unsigned char *)p)[1] << 8) |
|
|
(((unsigned char *)p)[2] << 16) |
|
|
(((unsigned char *)p)[3] << 24);
|
|
#endif
|
|
#else
|
|
#if LITTLE_ENDIAN
|
|
return *p;
|
|
#else
|
|
return ((unsigned char *)p)[0] |
|
|
(((unsigned char *)p)[1] << 8) |
|
|
(((unsigned char *)p)[2] << 16) |
|
|
(((unsigned char *)p)[3] << 24);
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
inline unsigned readlongBE(unsigned *p)
|
|
{
|
|
#if IN_LLVM
|
|
#if __BIG_ENDIAN__
|
|
return *p;
|
|
#else
|
|
return ((unsigned char *)p)[3] |
|
|
(((unsigned char *)p)[2] << 8) |
|
|
(((unsigned char *)p)[1] << 16) |
|
|
(((unsigned char *)p)[0] << 24);
|
|
#endif
|
|
#else
|
|
return ((unsigned char *)p)[3] |
|
|
(((unsigned char *)p)[2] << 8) |
|
|
(((unsigned char *)p)[1] << 16) |
|
|
(((unsigned char *)p)[0] << 24);
|
|
#endif
|
|
}
|
|
|
|
#if IN_LLVM
|
|
void Module::parse(bool gen_docs)
|
|
#else
|
|
void Module::parse()
|
|
#endif
|
|
{
|
|
//printf("Module::parse()\n");
|
|
|
|
char *srcname = srcfile->name->toChars();
|
|
//printf("Module::parse(srcname = '%s')\n", srcname);
|
|
|
|
unsigned char *buf = srcfile->buffer;
|
|
unsigned buflen = srcfile->len;
|
|
|
|
if (buflen >= 2)
|
|
{
|
|
/* Convert all non-UTF-8 formats to UTF-8.
|
|
* BOM : http://www.unicode.org/faq/utf_bom.html
|
|
* 00 00 FE FF UTF-32BE, big-endian
|
|
* FF FE 00 00 UTF-32LE, little-endian
|
|
* FE FF UTF-16BE, big-endian
|
|
* FF FE UTF-16LE, little-endian
|
|
* EF BB BF UTF-8
|
|
*/
|
|
|
|
unsigned le;
|
|
unsigned bom = 1; // assume there's a BOM
|
|
if (buf[0] == 0xFF && buf[1] == 0xFE)
|
|
{
|
|
if (buflen >= 4 && buf[2] == 0 && buf[3] == 0)
|
|
{ // UTF-32LE
|
|
le = 1;
|
|
|
|
Lutf32:
|
|
OutBuffer dbuf;
|
|
unsigned *pu = (unsigned *)(buf);
|
|
unsigned *pumax = &pu[buflen / 4];
|
|
|
|
if (buflen & 3)
|
|
{ error("odd length of UTF-32 char source %u", buflen);
|
|
fatal();
|
|
}
|
|
|
|
dbuf.reserve(buflen / 4);
|
|
for (pu += bom; pu < pumax; pu++)
|
|
{ unsigned u;
|
|
|
|
u = le ? readlongLE(pu) : readlongBE(pu);
|
|
if (u & ~0x7F)
|
|
{
|
|
if (u > 0x10FFFF)
|
|
{ error("UTF-32 value %08x greater than 0x10FFFF", u);
|
|
fatal();
|
|
}
|
|
dbuf.writeUTF8(u);
|
|
}
|
|
else
|
|
dbuf.writeByte(u);
|
|
}
|
|
dbuf.writeByte(0); // add 0 as sentinel for scanner
|
|
buflen = dbuf.offset - 1; // don't include sentinel in count
|
|
buf = (unsigned char *) dbuf.extractData();
|
|
}
|
|
else
|
|
{ // UTF-16LE (X86)
|
|
// Convert it to UTF-8
|
|
le = 1;
|
|
|
|
Lutf16:
|
|
OutBuffer dbuf;
|
|
unsigned short *pu = (unsigned short *)(buf);
|
|
unsigned short *pumax = &pu[buflen / 2];
|
|
|
|
if (buflen & 1)
|
|
{ error("odd length of UTF-16 char source %u", buflen);
|
|
fatal();
|
|
}
|
|
|
|
dbuf.reserve(buflen / 2);
|
|
for (pu += bom; pu < pumax; pu++)
|
|
{ unsigned u;
|
|
|
|
u = le ? readwordLE(pu) : readwordBE(pu);
|
|
if (u & ~0x7F)
|
|
{ if (u >= 0xD800 && u <= 0xDBFF)
|
|
{ unsigned u2;
|
|
|
|
if (++pu > pumax)
|
|
{ error("surrogate UTF-16 high value %04x at EOF", u);
|
|
fatal();
|
|
}
|
|
u2 = le ? readwordLE(pu) : readwordBE(pu);
|
|
if (u2 < 0xDC00 || u2 > 0xDFFF)
|
|
{ error("surrogate UTF-16 low value %04x out of range", u2);
|
|
fatal();
|
|
}
|
|
u = (u - 0xD7C0) << 10;
|
|
u |= (u2 - 0xDC00);
|
|
}
|
|
else if (u >= 0xDC00 && u <= 0xDFFF)
|
|
{ error("unpaired surrogate UTF-16 value %04x", u);
|
|
fatal();
|
|
}
|
|
else if (u == 0xFFFE || u == 0xFFFF)
|
|
{ error("illegal UTF-16 value %04x", u);
|
|
fatal();
|
|
}
|
|
dbuf.writeUTF8(u);
|
|
}
|
|
else
|
|
dbuf.writeByte(u);
|
|
}
|
|
dbuf.writeByte(0); // add 0 as sentinel for scanner
|
|
buflen = dbuf.offset - 1; // don't include sentinel in count
|
|
buf = (unsigned char *) dbuf.extractData();
|
|
}
|
|
}
|
|
else if (buf[0] == 0xFE && buf[1] == 0xFF)
|
|
{ // UTF-16BE
|
|
le = 0;
|
|
goto Lutf16;
|
|
}
|
|
else if (buflen >= 4 && buf[0] == 0 && buf[1] == 0 && buf[2] == 0xFE && buf[3] == 0xFF)
|
|
{ // UTF-32BE
|
|
le = 0;
|
|
goto Lutf32;
|
|
}
|
|
else if (buflen >= 3 && buf[0] == 0xEF && buf[1] == 0xBB && buf[2] == 0xBF)
|
|
{ // UTF-8
|
|
|
|
buf += 3;
|
|
buflen -= 3;
|
|
}
|
|
else
|
|
{
|
|
/* There is no BOM. Make use of Arcane Jill's insight that
|
|
* the first char of D source must be ASCII to
|
|
* figure out the encoding.
|
|
*/
|
|
|
|
bom = 0;
|
|
if (buflen >= 4)
|
|
{ if (buf[1] == 0 && buf[2] == 0 && buf[3] == 0)
|
|
{ // UTF-32LE
|
|
le = 1;
|
|
goto Lutf32;
|
|
}
|
|
else if (buf[0] == 0 && buf[1] == 0 && buf[2] == 0)
|
|
{ // UTF-32BE
|
|
le = 0;
|
|
goto Lutf32;
|
|
}
|
|
}
|
|
if (buflen >= 2)
|
|
{
|
|
if (buf[1] == 0)
|
|
{ // UTF-16LE
|
|
le = 1;
|
|
goto Lutf16;
|
|
}
|
|
else if (buf[0] == 0)
|
|
{ // UTF-16BE
|
|
le = 0;
|
|
goto Lutf16;
|
|
}
|
|
}
|
|
|
|
// It's UTF-8
|
|
if (buf[0] >= 0x80)
|
|
{ error("source file must start with BOM or ASCII character, not \\x%02X", buf[0]);
|
|
fatal();
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef IN_GCC
|
|
// dump utf-8 encoded source
|
|
if (global.params.dump_source)
|
|
{ // %% srcname could contain a path ...
|
|
d_gcc_dump_source(srcname, "utf-8", buf, buflen);
|
|
}
|
|
#endif
|
|
|
|
/* If it starts with the string "Ddoc", then it's a documentation
|
|
* source file.
|
|
*/
|
|
if (buflen >= 4 && memcmp(buf, "Ddoc", 4) == 0)
|
|
{
|
|
comment = buf + 4;
|
|
isDocFile = 1;
|
|
#if IN_DMD
|
|
if (!docfile)
|
|
setDocfile();
|
|
#endif
|
|
return;
|
|
}
|
|
#if IN_LLVM
|
|
Parser p(this, buf, buflen, gen_docs);
|
|
#else
|
|
Parser p(this, buf, buflen, docfile != NULL);
|
|
#endif
|
|
p.nextToken();
|
|
members = p.parseModule();
|
|
|
|
::free(srcfile->buffer);
|
|
srcfile->buffer = NULL;
|
|
srcfile->len = 0;
|
|
|
|
md = p.md;
|
|
numlines = p.loc.linnum;
|
|
|
|
DsymbolTable *dst;
|
|
|
|
if (md)
|
|
{ this->ident = md->id;
|
|
this->safe = md->safe;
|
|
Package *ppack = NULL;
|
|
dst = Package::resolve(md->packages, &this->parent, &ppack);
|
|
if (ppack && ppack->isModule())
|
|
{
|
|
error(loc, "package name '%s' in file %s conflicts with usage as a module name in file %s",
|
|
ppack->toChars(), srcname, ppack->isModule()->srcfile->toChars());
|
|
dst = modules;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dst = modules;
|
|
|
|
/* Check to see if module name is a valid identifier
|
|
*/
|
|
if (!Lexer::isValidIdentifier(this->ident->toChars()))
|
|
error("has non-identifier characters in filename, use module declaration instead");
|
|
}
|
|
|
|
// Update global list of modules
|
|
if (!dst->insert(this))
|
|
{
|
|
Dsymbol *prev = dst->lookup(ident);
|
|
assert(prev);
|
|
Module *mprev = prev->isModule();
|
|
if (mprev)
|
|
error(loc, "from file %s conflicts with another module %s from file %s",
|
|
srcname, mprev->toChars(), mprev->srcfile->toChars());
|
|
else
|
|
{
|
|
Package *pkg = prev->isPackage();
|
|
assert(pkg);
|
|
error(pkg->loc, "from file %s conflicts with package name %s",
|
|
srcname, pkg->toChars());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
amodules.push(this);
|
|
}
|
|
}
|
|
|
|
void Module::importAll(Scope *prevsc)
|
|
{
|
|
//printf("+Module::importAll(this = %p, '%s'): parent = %p\n", this, toChars(), parent);
|
|
|
|
if (scope)
|
|
return; // already done
|
|
|
|
if (isDocFile)
|
|
{
|
|
error("is a Ddoc file, cannot import it");
|
|
return;
|
|
}
|
|
|
|
/* Note that modules get their own scope, from scratch.
|
|
* This is so regardless of where in the syntax a module
|
|
* gets imported, it is unaffected by context.
|
|
* Ignore prevsc.
|
|
*/
|
|
Scope *sc = Scope::createGlobal(this); // create root scope
|
|
|
|
// Add import of "object", even for the "object" module.
|
|
// If it isn't there, some compiler rewrites, like
|
|
// classinst == classinst -> .object.opEquals(classinst, classinst)
|
|
// would fail inside object.d.
|
|
if (members->dim == 0 || ((*members)[0])->ident != Id::object)
|
|
{
|
|
Import *im = new Import(0, NULL, Id::object, NULL, 0);
|
|
members->shift(im);
|
|
}
|
|
|
|
if (!symtab)
|
|
{
|
|
// Add all symbols into module's symbol table
|
|
symtab = new DsymbolTable();
|
|
for (size_t i = 0; i < members->dim; i++)
|
|
{
|
|
Dsymbol *s = (*members)[i];
|
|
s->addMember(NULL, sc->scopesym, 1);
|
|
}
|
|
}
|
|
// anything else should be run after addMember, so version/debug symbols are defined
|
|
|
|
/* Set scope for the symbols so that if we forward reference
|
|
* a symbol, it can possibly be resolved on the spot.
|
|
* If this works out well, it can be extended to all modules
|
|
* before any semantic() on any of them.
|
|
*/
|
|
setScope(sc); // remember module scope for semantic
|
|
for (size_t i = 0; i < members->dim; i++)
|
|
{ Dsymbol *s = (*members)[i];
|
|
s->setScope(sc);
|
|
}
|
|
|
|
for (size_t i = 0; i < members->dim; i++)
|
|
{
|
|
Dsymbol *s = (*members)[i];
|
|
s->importAll(sc);
|
|
}
|
|
|
|
sc = sc->pop();
|
|
sc->pop(); // 2 pops because Scope::createGlobal() created 2
|
|
}
|
|
|
|
void Module::semantic(Scope* unused_sc)
|
|
{
|
|
if (semanticstarted)
|
|
return;
|
|
|
|
//printf("+Module::semantic(this = %p, '%s'): parent = %p\n", this, toChars(), parent);
|
|
semanticstarted = 1;
|
|
|
|
// Note that modules get their own scope, from scratch.
|
|
// This is so regardless of where in the syntax a module
|
|
// gets imported, it is unaffected by context.
|
|
Scope *sc = scope; // see if already got one from importAll()
|
|
if (!sc)
|
|
{ printf("test2\n");
|
|
Scope::createGlobal(this); // create root scope
|
|
}
|
|
|
|
//printf("Module = %p, linkage = %d\n", sc->scopesym, sc->linkage);
|
|
|
|
#if 0
|
|
// Add import of "object" if this module isn't "object"
|
|
if (ident != Id::object)
|
|
{
|
|
Import *im = new Import(0, NULL, Id::object, NULL, 0);
|
|
members->shift(im);
|
|
}
|
|
|
|
// Add all symbols into module's symbol table
|
|
symtab = new DsymbolTable();
|
|
for (size_t i = 0; i < members->dim; i++)
|
|
{ Dsymbol *s = (Dsymbol *)members->data[i];
|
|
s->addMember(NULL, sc->scopesym, 1);
|
|
}
|
|
|
|
/* Set scope for the symbols so that if we forward reference
|
|
* a symbol, it can possibly be resolved on the spot.
|
|
* If this works out well, it can be extended to all modules
|
|
* before any semantic() on any of them.
|
|
*/
|
|
for (size_t i = 0; i < members->dim; i++)
|
|
{ Dsymbol *s = (Dsymbol *)members->data[i];
|
|
s->setScope(sc);
|
|
}
|
|
#endif
|
|
|
|
// Do semantic() on members that don't depend on others
|
|
for (size_t i = 0; i < members->dim; i++)
|
|
{ Dsymbol *s = (*members)[i];
|
|
|
|
//printf("\tModule('%s'): '%s'.semantic0()\n", toChars(), s->toChars());
|
|
s->semantic0(sc);
|
|
}
|
|
|
|
// Pass 1 semantic routines: do public side of the definition
|
|
for (size_t i = 0; i < members->dim; i++)
|
|
{ Dsymbol *s = (*members)[i];
|
|
|
|
//printf("\tModule('%s'): '%s'.semantic()\n", toChars(), s->toChars());
|
|
s->semantic(sc);
|
|
runDeferredSemantic();
|
|
}
|
|
|
|
if (!scope)
|
|
{ sc = sc->pop();
|
|
sc->pop(); // 2 pops because Scope::createGlobal() created 2
|
|
}
|
|
semanticRun = semanticstarted;
|
|
//printf("-Module::semantic(this = %p, '%s'): parent = %p\n", this, toChars(), parent);
|
|
}
|
|
|
|
void Module::semantic2(Scope* unused_sc)
|
|
{
|
|
if (deferred.dim)
|
|
{
|
|
for (size_t i = 0; i < deferred.dim; i++)
|
|
{
|
|
Dsymbol *sd = deferred[i];
|
|
|
|
sd->error("unable to resolve forward reference in definition");
|
|
}
|
|
return;
|
|
}
|
|
//printf("Module::semantic2('%s'): parent = %p\n", toChars(), parent);
|
|
if (semanticRun == 0) // semantic() not completed yet - could be recursive call
|
|
return;
|
|
if (semanticstarted >= 2)
|
|
return;
|
|
assert(semanticstarted == 1);
|
|
semanticstarted = 2;
|
|
|
|
// Note that modules get their own scope, from scratch.
|
|
// This is so regardless of where in the syntax a module
|
|
// gets imported, it is unaffected by context.
|
|
Scope *sc = Scope::createGlobal(this); // create root scope
|
|
//printf("Module = %p\n", sc.scopesym);
|
|
|
|
// Pass 2 semantic routines: do initializers and function bodies
|
|
for (size_t i = 0; i < members->dim; i++)
|
|
{ Dsymbol *s;
|
|
|
|
s = (*members)[i];
|
|
s->semantic2(sc);
|
|
}
|
|
|
|
sc = sc->pop();
|
|
sc->pop();
|
|
semanticRun = semanticstarted;
|
|
//printf("-Module::semantic2('%s'): parent = %p\n", toChars(), parent);
|
|
}
|
|
|
|
void Module::semantic3(Scope* unused_sc)
|
|
{
|
|
//printf("Module::semantic3('%s'): parent = %p\n", toChars(), parent);
|
|
if (semanticstarted >= 3)
|
|
return;
|
|
assert(semanticstarted == 2);
|
|
semanticstarted = 3;
|
|
|
|
// Note that modules get their own scope, from scratch.
|
|
// This is so regardless of where in the syntax a module
|
|
// gets imported, it is unaffected by context.
|
|
Scope *sc = Scope::createGlobal(this); // create root scope
|
|
//printf("Module = %p\n", sc.scopesym);
|
|
|
|
// Pass 3 semantic routines: do initializers and function bodies
|
|
for (size_t i = 0; i < members->dim; i++)
|
|
{ Dsymbol *s;
|
|
|
|
s = (*members)[i];
|
|
//printf("Module %s: %s.semantic3()\n", toChars(), s->toChars());
|
|
s->semantic3(sc);
|
|
}
|
|
|
|
sc = sc->pop();
|
|
sc->pop();
|
|
semanticRun = semanticstarted;
|
|
}
|
|
|
|
void Module::inlineScan()
|
|
{
|
|
if (semanticstarted >= 4)
|
|
return;
|
|
assert(semanticstarted == 3);
|
|
semanticstarted = 4;
|
|
|
|
// Note that modules get their own scope, from scratch.
|
|
// This is so regardless of where in the syntax a module
|
|
// gets imported, it is unaffected by context.
|
|
//printf("Module = %p\n", sc.scopesym);
|
|
|
|
for (size_t i = 0; i < members->dim; i++)
|
|
{ Dsymbol *s = (*members)[i];
|
|
//if (global.params.verbose)
|
|
//printf("inline scan symbol %s\n", s->toChars());
|
|
|
|
s->inlineScan();
|
|
}
|
|
semanticRun = semanticstarted;
|
|
}
|
|
|
|
/****************************************************
|
|
*/
|
|
|
|
// is this used anywhere?
|
|
/*
|
|
void Module::gensymfile()
|
|
{
|
|
OutBuffer buf;
|
|
HdrGenState hgs;
|
|
|
|
//printf("Module::gensymfile()\n");
|
|
|
|
buf.printf("// Sym file generated from '%s'", srcfile->toChars());
|
|
buf.writenl();
|
|
|
|
for (size_t i = 0; i < members->dim; i++)
|
|
{ Dsymbol *s = (*members)[i];
|
|
|
|
s->toCBuffer(&buf, &hgs);
|
|
}
|
|
|
|
// Transfer image to file
|
|
symfile->setbuffer(buf.data, buf.offset);
|
|
buf.data = NULL;
|
|
|
|
symfile->writev();
|
|
}*/
|
|
|
|
/**********************************
|
|
* Determine if we need to generate an instance of ModuleInfo
|
|
* for this Module.
|
|
*/
|
|
|
|
int Module::needModuleInfo()
|
|
{
|
|
//printf("needModuleInfo() %s, %d, %d\n", toChars(), needmoduleinfo, global.params.cov);
|
|
return needmoduleinfo;
|
|
}
|
|
|
|
Dsymbol *Module::search(Loc loc, Identifier *ident, int flags)
|
|
{
|
|
/* Since modules can be circularly referenced,
|
|
* need to stop infinite recursive searches.
|
|
* This is done with the cache.
|
|
*/
|
|
|
|
//printf("%s Module::search('%s', flags = %d) insearch = %d\n", toChars(), ident->toChars(), flags, insearch);
|
|
Dsymbol *s;
|
|
if (insearch)
|
|
s = NULL;
|
|
else if (searchCacheIdent == ident && searchCacheFlags == flags)
|
|
{
|
|
s = searchCacheSymbol;
|
|
//printf("%s Module::search('%s', flags = %d) insearch = %d searchCacheSymbol = %s\n", toChars(), ident->toChars(), flags, insearch, searchCacheSymbol ? searchCacheSymbol->toChars() : "null");
|
|
}
|
|
else
|
|
{
|
|
insearch = 1;
|
|
s = ScopeDsymbol::search(loc, ident, flags);
|
|
insearch = 0;
|
|
|
|
searchCacheIdent = ident;
|
|
searchCacheSymbol = s;
|
|
searchCacheFlags = flags;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
Dsymbol *Module::symtabInsert(Dsymbol *s)
|
|
{
|
|
searchCacheIdent = 0; // symbol is inserted, so invalidate cache
|
|
return Package::symtabInsert(s);
|
|
}
|
|
|
|
void Module::clearCache()
|
|
{
|
|
for (size_t i = 0; i < amodules.dim; i++)
|
|
{ Module *m = amodules[i];
|
|
m->searchCacheIdent = NULL;
|
|
}
|
|
}
|
|
|
|
/*******************************************
|
|
* Can't run semantic on s now, try again later.
|
|
*/
|
|
|
|
void Module::addDeferredSemantic(Dsymbol *s)
|
|
{
|
|
// Don't add it if it is already there
|
|
for (size_t i = 0; i < deferred.dim; i++)
|
|
{
|
|
Dsymbol *sd = deferred[i];
|
|
|
|
if (sd == s)
|
|
return;
|
|
}
|
|
|
|
//printf("Module::addDeferredSemantic('%s')\n", s->toChars());
|
|
deferred.push(s);
|
|
}
|
|
|
|
|
|
/******************************************
|
|
* Run semantic() on deferred symbols.
|
|
*/
|
|
|
|
void Module::runDeferredSemantic()
|
|
{
|
|
if (dprogress == 0)
|
|
return;
|
|
|
|
static int nested;
|
|
if (nested)
|
|
return;
|
|
//if (deferred.dim) printf("+Module::runDeferredSemantic('%s'), len = %d\n", toChars(), deferred.dim);
|
|
nested++;
|
|
|
|
size_t len;
|
|
do
|
|
{
|
|
dprogress = 0;
|
|
len = deferred.dim;
|
|
if (!len)
|
|
break;
|
|
|
|
Dsymbol **todo;
|
|
Dsymbol *tmp;
|
|
if (len == 1)
|
|
{
|
|
todo = &tmp;
|
|
}
|
|
else
|
|
{
|
|
todo = (Dsymbol **)alloca(len * sizeof(Dsymbol *));
|
|
assert(todo);
|
|
}
|
|
memcpy(todo, deferred.tdata(), len * sizeof(Dsymbol *));
|
|
deferred.setDim(0);
|
|
|
|
for (size_t i = 0; i < len; i++)
|
|
{
|
|
Dsymbol *s = todo[i];
|
|
|
|
s->semantic(NULL);
|
|
//printf("deferred: %s, parent = %s\n", s->toChars(), s->parent->toChars());
|
|
}
|
|
//printf("\tdeferred.dim = %d, len = %d, dprogress = %d\n", deferred.dim, len, dprogress);
|
|
} while (deferred.dim < len || dprogress); // while making progress
|
|
nested--;
|
|
//printf("-Module::runDeferredSemantic('%s'), len = %d\n", toChars(), deferred.dim);
|
|
}
|
|
|
|
/************************************
|
|
* Recursively look at every module this module imports,
|
|
* return TRUE if it imports m.
|
|
* Can be used to detect circular imports.
|
|
*/
|
|
|
|
int Module::imports(Module *m)
|
|
{
|
|
//printf("%s Module::imports(%s)\n", toChars(), m->toChars());
|
|
#if 0
|
|
for (size_t i = 0; i < aimports.dim; i++)
|
|
{ Module *mi = (Module *)aimports.data[i];
|
|
printf("\t[%d] %s\n", i, mi->toChars());
|
|
}
|
|
#endif
|
|
for (size_t i = 0; i < aimports.dim; i++)
|
|
{ Module *mi = aimports[i];
|
|
if (mi == m)
|
|
return TRUE;
|
|
if (!mi->insearch)
|
|
{
|
|
mi->insearch = 1;
|
|
int r = mi->imports(m);
|
|
if (r)
|
|
return r;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/*************************************
|
|
* Return !=0 if module imports itself.
|
|
*/
|
|
|
|
int Module::selfImports()
|
|
{
|
|
//printf("Module::selfImports() %s\n", toChars());
|
|
if (!selfimports)
|
|
{
|
|
for (size_t i = 0; i < amodules.dim; i++)
|
|
{ Module *mi = amodules[i];
|
|
//printf("\t[%d] %s\n", i, mi->toChars());
|
|
mi->insearch = 0;
|
|
}
|
|
|
|
selfimports = imports(this) + 1;
|
|
|
|
for (size_t i = 0; i < amodules.dim; i++)
|
|
{ Module *mi = amodules[i];
|
|
//printf("\t[%d] %s\n", i, mi->toChars());
|
|
mi->insearch = 0;
|
|
}
|
|
}
|
|
return selfimports - 1;
|
|
}
|
|
|
|
|
|
/* =========================== ModuleDeclaration ===================== */
|
|
|
|
ModuleDeclaration::ModuleDeclaration(Identifiers *packages, Identifier *id, bool safe)
|
|
{
|
|
this->packages = packages;
|
|
this->id = id;
|
|
this->safe = safe;
|
|
}
|
|
|
|
char *ModuleDeclaration::toChars()
|
|
{
|
|
OutBuffer buf;
|
|
|
|
if (packages && packages->dim)
|
|
{
|
|
for (size_t i = 0; i < packages->dim; i++)
|
|
{ Identifier *pid = (*packages)[i];
|
|
|
|
buf.writestring(pid->toChars());
|
|
buf.writeByte('.');
|
|
}
|
|
}
|
|
buf.writestring(id->toChars());
|
|
buf.writeByte(0);
|
|
return (char *)buf.extractData();
|
|
}
|
|
|
|
/* =========================== Package ===================== */
|
|
|
|
Package::Package(Identifier *ident)
|
|
: ScopeDsymbol(ident)
|
|
{
|
|
}
|
|
|
|
|
|
const char *Package::kind()
|
|
{
|
|
return "package";
|
|
}
|
|
|
|
|
|
DsymbolTable *Package::resolve(Identifiers *packages, Dsymbol **pparent, Package **ppkg)
|
|
{
|
|
DsymbolTable *dst = Module::modules;
|
|
Dsymbol *parent = NULL;
|
|
|
|
//printf("Package::resolve()\n");
|
|
if (ppkg)
|
|
*ppkg = NULL;
|
|
|
|
if (packages)
|
|
{
|
|
for (size_t i = 0; i < packages->dim; i++)
|
|
{ Identifier *pid = (*packages)[i];
|
|
Dsymbol *p;
|
|
|
|
p = dst->lookup(pid);
|
|
if (!p)
|
|
{
|
|
p = new Package(pid);
|
|
dst->insert(p);
|
|
p->parent = parent;
|
|
((ScopeDsymbol *)p)->symtab = new DsymbolTable();
|
|
}
|
|
else
|
|
{
|
|
assert(p->isPackage());
|
|
// It might already be a module, not a package, but that needs
|
|
// to be checked at a higher level, where a nice error message
|
|
// can be generated.
|
|
// dot net needs modules and packages with same name
|
|
}
|
|
parent = p;
|
|
dst = ((Package *)p)->symtab;
|
|
if (ppkg && !*ppkg)
|
|
*ppkg = (Package *)p;
|
|
#if TARGET_NET
|
|
#else
|
|
if (p->isModule())
|
|
{ // Return the module so that a nice error message can be generated
|
|
if (ppkg)
|
|
*ppkg = (Package *)p;
|
|
break;
|
|
}
|
|
#endif
|
|
}
|
|
if (pparent)
|
|
{
|
|
*pparent = parent;
|
|
}
|
|
}
|
|
return dst;
|
|
}
|