| |
| /* Compiler implementation of the D programming language |
| * Copyright (C) 1999-2019 by The D Language Foundation, All Rights Reserved |
| * written by Walter Bright |
| * http://www.digitalmars.com |
| * Distributed under the Boost Software License, Version 1.0. |
| * http://www.boost.org/LICENSE_1_0.txt |
| * https://github.com/D-Programming-Language/dmd/blob/master/src/module.c |
| */ |
| |
| #include "root/dsystem.h" |
| #include "root/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 "expression.h" |
| #include "lexer.h" |
| #include "attrib.h" |
| |
| AggregateDeclaration *Module::moduleinfo; |
| |
| Module *Module::rootModule; |
| DsymbolTable *Module::modules; |
| Modules Module::amodules; |
| |
| Dsymbols Module::deferred; // deferred Dsymbol's needing semantic() run on them |
| Dsymbols Module::deferred2; // deferred Dsymbol's needing semantic2() run on them |
| Dsymbols Module::deferred3; // deferred Dsymbol's needing semantic3() run on them |
| unsigned Module::dprogress; |
| |
| const char *lookForSourceFile(const char **path, const char *filename); |
| StringExp *semanticString(Scope *sc, Expression *exp, const char *s); |
| |
| void Module::_init() |
| { |
| modules = new DsymbolTable(); |
| } |
| |
| Module::Module(const char *filename, Identifier *ident, int doDocComment, int doHdrGen) |
| : Package(ident) |
| { |
| const char *srcfilename; |
| |
| // printf("Module::Module(filename = '%s', ident = '%s')\n", filename, ident->toChars()); |
| this->arg = filename; |
| md = NULL; |
| errors = 0; |
| numlines = 0; |
| members = NULL; |
| isDocFile = 0; |
| isPackageFile = false; |
| needmoduleinfo = 0; |
| selfimports = 0; |
| rootimports = 0; |
| insearch = 0; |
| searchCacheIdent = NULL; |
| searchCacheSymbol = NULL; |
| searchCacheFlags = 0; |
| decldefs = NULL; |
| sictor = NULL; |
| sctor = NULL; |
| sdtor = NULL; |
| ssharedctor = NULL; |
| sshareddtor = NULL; |
| stest = NULL; |
| sfilename = NULL; |
| importedFrom = NULL; |
| srcfile = NULL; |
| srcfilePath = NULL; |
| docfile = NULL; |
| |
| debuglevel = 0; |
| debugids = NULL; |
| debugidsNot = NULL; |
| versionlevel = 0; |
| versionids = NULL; |
| versionidsNot = NULL; |
| |
| macrotable = NULL; |
| escapetable = NULL; |
| doppelganger = 0; |
| cov = NULL; |
| covb = NULL; |
| |
| nameoffset = 0; |
| namelen = 0; |
| |
| srcfilename = FileName::defaultExt(filename, global.mars_ext); |
| |
| if (global.run_noext && global.params.run && |
| !FileName::ext(filename) && |
| FileName::exists(srcfilename) == 0 && |
| FileName::exists(filename) == 1) |
| { |
| FileName::free(srcfilename); |
| srcfilename = FileName::removeExt(filename); // just does a mem.strdup(filename) |
| } |
| else if (!FileName::equalsExt(srcfilename, global.mars_ext) && |
| !FileName::equalsExt(srcfilename, global.hdr_ext) && |
| !FileName::equalsExt(srcfilename, "dd")) |
| { |
| error("source file name '%s' must have .%s extension", srcfilename, global.mars_ext); |
| fatal(); |
| } |
| srcfile = new File(srcfilename); |
| if (!FileName::absolute(srcfilename)) |
| srcfilePath = getcwd(NULL, 0); |
| |
| objfile = setOutfile(global.params.objname, global.params.objdir, filename, global.obj_ext); |
| |
| if (doDocComment) |
| setDocfile(); |
| |
| if (doHdrGen) |
| hdrfile = setOutfile(global.params.hdrname, global.params.hdrdir, arg, global.hdr_ext); |
| |
| //objfile = new File(objfilename); |
| } |
| |
| Module *Module::create(const char *filename, Identifier *ident, int doDocComment, int doHdrGen) |
| { |
| return new Module(filename, ident, doDocComment, doHdrGen); |
| } |
| |
| void Module::setDocfile() |
| { |
| docfile = setOutfile(global.params.docname, global.params.docdir, arg, global.doc_ext); |
| } |
| |
| /********************************************* |
| * Combines things into output file name for .html and .di files. |
| * Input: |
| * name Command line name given for the file, NULL if none |
| * dir Command line directory given for the file, NULL if none |
| * arg Name of the source file |
| * ext File name extension to use if 'name' is NULL |
| * global.params.preservePaths get output path from arg |
| * srcfile Input file - output file name must not match input file |
| */ |
| |
| File *Module::setOutfile(const char *name, const char *dir, const char *arg, const char *ext) |
| { |
| const char *docfilename; |
| |
| if (name) |
| { |
| docfilename = name; |
| } |
| else |
| { |
| const char *argdoc; |
| if (global.params.preservePaths) |
| argdoc = arg; |
| else |
| argdoc = FileName::name(arg); |
| |
| // If argdoc doesn't have an absolute path, make it relative to dir |
| if (!FileName::absolute(argdoc)) |
| { //FileName::ensurePathExists(dir); |
| argdoc = FileName::combine(dir, argdoc); |
| } |
| docfilename = FileName::forceExt(argdoc, ext); |
| } |
| |
| if (FileName::equals(docfilename, srcfile->name->str)) |
| { |
| error("source file and output file have same name '%s'", srcfile->name->str); |
| fatal(); |
| } |
| |
| return new File(docfilename); |
| } |
| |
| void Module::deleteObjFile() |
| { |
| if (global.params.obj) |
| objfile->remove(); |
| if (docfile) |
| docfile->remove(); |
| } |
| |
| const char *Module::kind() const |
| { |
| return "module"; |
| } |
| |
| static void checkModFileAlias(OutBuffer *buf, OutBuffer *dotmods, |
| Array<const char *> *ms, size_t msdim, const char *p) |
| { |
| /* Check and replace the contents of buf[] with |
| * an alias string from global.params.modFileAliasStrings[] |
| */ |
| dotmods->writestring(p); |
| for (size_t j = msdim; j--;) |
| { |
| const char *m = (*ms)[j]; |
| const char *q = strchr(m, '='); |
| assert(q); |
| if (dotmods->offset == (size_t)(q - m) && memcmp(dotmods->peekString(), m, q - m) == 0) |
| { |
| buf->reset(); |
| size_t qlen = strlen(q + 1); |
| if (qlen && (q[qlen] == '/' || q[qlen] == '\\')) |
| --qlen; // remove trailing separator |
| buf->write(q + 1, qlen); |
| break; // last matching entry in ms[] wins |
| } |
| } |
| dotmods->writeByte('.'); |
| } |
| |
| Module *Module::load(Loc loc, Identifiers *packages, Identifier *ident) |
| { |
| //printf("Module::load(ident = '%s')\n", ident->toChars()); |
| |
| // Build module filename by turning: |
| // foo.bar.baz |
| // into: |
| // foo\bar\baz |
| const char *filename = ident->toChars(); |
| if (packages && packages->dim) |
| { |
| OutBuffer buf; |
| OutBuffer dotmods; |
| Array<const char *> *ms = global.params.modFileAliasStrings; |
| const size_t msdim = ms ? ms->dim : 0; |
| |
| for (size_t i = 0; i < packages->dim; i++) |
| { |
| Identifier *pid = (*packages)[i]; |
| const char *p = pid->toChars(); |
| buf.writestring(p); |
| if (msdim) |
| checkModFileAlias(&buf, &dotmods, ms, msdim, p); |
| #if _WIN32 |
| buf.writeByte('\\'); |
| #else |
| buf.writeByte('/'); |
| #endif |
| } |
| buf.writestring(filename); |
| if (msdim) |
| checkModFileAlias(&buf, &dotmods, ms, msdim, filename); |
| buf.writeByte(0); |
| filename = (char *)buf.extractData(); |
| } |
| |
| Module *m = new Module(filename, ident, 0, 0); |
| m->loc = loc; |
| |
| /* Look for the source file |
| */ |
| const char *path; |
| const char *result = lookForSourceFile(&path, filename); |
| if (result) |
| { |
| m->srcfile = new File(result); |
| if (path) |
| m->srcfilePath = path; |
| else if (!FileName::absolute(result)) |
| m->srcfilePath = getcwd(NULL, 0); |
| } |
| |
| if (!m->read(loc)) |
| return NULL; |
| |
| if (global.params.verbose) |
| { |
| OutBuffer buf; |
| if (packages) |
| { |
| for (size_t i = 0; i < packages->dim; i++) |
| { |
| Identifier *pid = (*packages)[i]; |
| buf.writestring(pid->toChars()); |
| buf.writeByte('.'); |
| } |
| } |
| buf.printf("%s\t(%s)", ident->toChars(), m->srcfile->toChars()); |
| message("import %s", buf.peekString()); |
| } |
| |
| m = m->parse(); |
| |
| Compiler::loadModule(m); |
| |
| return m; |
| } |
| |
| bool Module::read(Loc loc) |
| { |
| //printf("Module::read('%s') file '%s'\n", toChars(), srcfile->toChars()); |
| if (srcfile->read()) |
| { |
| if (!strcmp(srcfile->toChars(), "object.d")) |
| { |
| ::error(loc, "cannot find source code for runtime library file 'object.d'"); |
| errorSupplemental(loc, "dmd might not be correctly installed. Run 'dmd -man' for installation instructions."); |
| const char *dmdConfFile = global.inifilename ? FileName::canonicalName(global.inifilename) : NULL; |
| errorSupplemental(loc, "config file: %s", dmdConfFile ? dmdConfFile : "not found"); |
| } |
| else |
| { |
| // if module is not named 'package' but we're trying to read 'package.d', we're looking for a package module |
| bool isPackageMod = (strcmp(toChars(), "package") != 0) && |
| (strcmp(srcfile->name->name(), "package.d") == 0 || (strcmp(srcfile->name->name(), "package.di") == 0)); |
| |
| if (isPackageMod) |
| ::error(loc, "importing package '%s' requires a 'package.d' file which cannot be found in '%s'", |
| toChars(), srcfile->toChars()); |
| else |
| 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++) |
| { |
| const char *p = (*global.path)[i]; |
| fprintf(stderr, "import path[%llu] = %s\n", (ulonglong)i, p); |
| } |
| } |
| else |
| fprintf(stderr, "Specify path to file '%s' with -I switch\n", srcfile->toChars()); |
| fatal(); |
| } |
| return false; |
| } |
| return true; |
| } |
| |
| Module *Module::parse() |
| { |
| //printf("Module::parse(srcfile='%s') this=%p\n", srcfile->name->toChars(), this); |
| |
| const char *srcname = srcfile->name->toChars(); |
| //printf("Module::parse(srcname = '%s')\n", srcname); |
| |
| isPackageFile = (strcmp(srcfile->name->name(), "package.d") == 0 || |
| strcmp(srcfile->name->name(), "package.di") == 0); |
| |
| utf8_t *buf = (utf8_t *)srcfile->buffer; |
| size_t 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 ? Port::readlongLE(pu) : Port::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 = (utf8_t *) 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 ? Port::readwordLE(pu) : Port::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 ? Port::readwordLE(pu) : Port::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 = (utf8_t *) 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(); |
| } |
| } |
| } |
| |
| /* 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 (!docfile) |
| setDocfile(); |
| return this; |
| } |
| { |
| Parser p(this, buf, buflen, docfile != NULL); |
| p.nextToken(); |
| members = p.parseModule(); |
| md = p.md; |
| numlines = p.scanloc.linnum; |
| if (p.errors) |
| ++global.errors; |
| } |
| |
| if (srcfile->ref == 0) |
| ::free(srcfile->buffer); |
| srcfile->buffer = NULL; |
| srcfile->len = 0; |
| |
| /* The symbol table into which the module is to be inserted. |
| */ |
| DsymbolTable *dst; |
| |
| if (md) |
| { |
| /* A ModuleDeclaration, md, was provided. |
| * The ModuleDeclaration sets the packages this module appears in, and |
| * the name of this module. |
| */ |
| this->ident = md->id; |
| Package *ppack = NULL; |
| dst = Package::resolve(md->packages, &this->parent, &ppack); |
| assert(dst); |
| |
| Module *m = ppack ? ppack->isModule() : NULL; |
| if (m && (strcmp(m->srcfile->name->name(), "package.d") != 0 && |
| strcmp(m->srcfile->name->name(), "package.di") != 0)) |
| { |
| ::error(md->loc, "package name '%s' conflicts with usage as a module name in file %s", |
| ppack->toPrettyChars(), m->srcfile->toChars()); |
| } |
| } |
| else |
| { |
| /* The name of the module is set to the source file name. |
| * There are no packages. |
| */ |
| dst = modules; // and so this module goes into global module symbol table |
| |
| /* Check to see if module name is a valid identifier |
| */ |
| if (!Identifier::isValidIdentifier(this->ident->toChars())) |
| error("has non-identifier characters in filename, use module declaration instead"); |
| } |
| |
| // Insert module into the symbol table |
| Dsymbol *s = this; |
| if (isPackageFile) |
| { |
| /* If the source tree is as follows: |
| * pkg/ |
| * +- package.d |
| * +- common.d |
| * the 'pkg' will be incorporated to the internal package tree in two ways: |
| * import pkg; |
| * and: |
| * import pkg.common; |
| * |
| * If both are used in one compilation, 'pkg' as a module (== pkg/package.d) |
| * and a package name 'pkg' will conflict each other. |
| * |
| * To avoid the conflict: |
| * 1. If preceding package name insertion had occurred by Package::resolve, |
| * later package.d loading will change Package::isPkgMod to PKGmodule and set Package::mod. |
| * 2. Otherwise, 'package.d' wrapped by 'Package' is inserted to the internal tree in here. |
| */ |
| Package *p = new Package(ident); |
| p->parent = this->parent; |
| p->isPkgMod = PKGmodule; |
| p->mod = this; |
| p->tag = this->tag; // reuse the same package tag |
| p->symtab = new DsymbolTable(); |
| s = p; |
| } |
| if (!dst->insert(s)) |
| { |
| /* It conflicts with a name that is already in the symbol table. |
| * Figure out what went wrong, and issue error message. |
| */ |
| Dsymbol *prev = dst->lookup(ident); |
| assert(prev); |
| if (Module *mprev = prev->isModule()) |
| { |
| if (FileName::compare(srcname, mprev->srcfile->toChars()) != 0) |
| error(loc, "from file %s conflicts with another module %s from file %s", |
| srcname, mprev->toChars(), mprev->srcfile->toChars()); |
| else if (isRoot() && mprev->isRoot()) |
| error(loc, "from file %s is specified twice on the command line", |
| srcname); |
| else |
| error(loc, "from file %s must be imported with 'import %s;'", |
| srcname, toPrettyChars()); |
| |
| // Bugzilla 14446: Return previously parsed module to avoid AST duplication ICE. |
| return mprev; |
| } |
| else if (Package *pkg = prev->isPackage()) |
| { |
| if (pkg->isPkgMod == PKGunknown && isPackageFile) |
| { |
| /* If the previous inserted Package is not yet determined as package.d, |
| * link it to the actual module. |
| */ |
| pkg->isPkgMod = PKGmodule; |
| pkg->mod = this; |
| pkg->tag = this->tag; // reuse the same package tag |
| } |
| else |
| error(md ? md->loc : loc, "from file %s conflicts with package name %s", |
| srcname, pkg->toChars()); |
| } |
| else |
| assert(global.errors); |
| } |
| else |
| { |
| // Add to global array of all modules |
| amodules.push(this); |
| } |
| return this; |
| } |
| |
| void Module::importAll(Scope *) |
| { |
| //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 |
| |
| if (md && md->msg) |
| md->msg = semanticString(sc, md->msg, "deprecation message"); |
| |
| // 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(Loc(), 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(sc, sc->scopesym); |
| } |
| } |
| // 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 *) |
| { |
| if (semanticRun != PASSinit) |
| return; |
| |
| //printf("+Module::semantic(this = %p, '%s'): parent = %p\n", this, toChars(), parent); |
| semanticRun = PASSsemantic; |
| |
| // 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) |
| { |
| Scope::createGlobal(this); // create root scope |
| } |
| |
| //printf("Module = %p, linkage = %d\n", sc->scopesym, sc->linkage); |
| |
| // 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 (userAttribDecl) |
| { |
| userAttribDecl->semantic(sc); |
| } |
| |
| if (!_scope) |
| { |
| sc = sc->pop(); |
| sc->pop(); // 2 pops because Scope::createGlobal() created 2 |
| } |
| semanticRun = PASSsemanticdone; |
| //printf("-Module::semantic(this = %p, '%s'): parent = %p\n", this, toChars(), parent); |
| } |
| |
| void Module::semantic2(Scope*) |
| { |
| //printf("Module::semantic2('%s'): parent = %p\n", toChars(), parent); |
| if (semanticRun != PASSsemanticdone) // semantic() not completed yet - could be recursive call |
| return; |
| semanticRun = PASSsemantic2; |
| |
| // 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 = (*members)[i]; |
| s->semantic2(sc); |
| } |
| |
| if (userAttribDecl) |
| { |
| userAttribDecl->semantic2(sc); |
| } |
| |
| sc = sc->pop(); |
| sc->pop(); |
| semanticRun = PASSsemantic2done; |
| //printf("-Module::semantic2('%s'): parent = %p\n", toChars(), parent); |
| } |
| |
| void Module::semantic3(Scope*) |
| { |
| //printf("Module::semantic3('%s'): parent = %p\n", toChars(), parent); |
| if (semanticRun != PASSsemantic2done) |
| return; |
| semanticRun = PASSsemantic3; |
| |
| // 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 = (*members)[i]; |
| //printf("Module %s: %s.semantic3()\n", toChars(), s->toChars()); |
| s->semantic3(sc); |
| |
| runDeferredSemantic2(); |
| } |
| |
| if (userAttribDecl) |
| { |
| userAttribDecl->semantic3(sc); |
| } |
| |
| sc = sc->pop(); |
| sc->pop(); |
| semanticRun = PASSsemantic3done; |
| } |
| |
| /********************************** |
| * 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 || global.params.cov; |
| } |
| |
| Dsymbol *Module::search(const 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 = x%x) insearch = %d\n", toChars(), ident->toChars(), flags, insearch); |
| if (insearch) |
| return NULL; |
| |
| /* Qualified module searches always search their imports, |
| * even if SearchLocalsOnly |
| */ |
| if (!(flags & SearchUnqualifiedModule)) |
| flags &= ~(SearchUnqualifiedModule | SearchLocalsOnly); |
| |
| if (searchCacheIdent == ident && searchCacheFlags == flags) |
| { |
| //printf("%s Module::search('%s', flags = %d) insearch = %d searchCacheSymbol = %s\n", |
| // toChars(), ident->toChars(), flags, insearch, searchCacheSymbol ? searchCacheSymbol->toChars() : "null"); |
| return searchCacheSymbol; |
| } |
| |
| unsigned int errors = global.errors; |
| |
| insearch = 1; |
| Dsymbol *s = ScopeDsymbol::search(loc, ident, flags); |
| insearch = 0; |
| |
| if (errors == global.errors) |
| { |
| // Bugzilla 10752: We can cache the result only when it does not cause |
| // access error so the side-effect should be reproduced in later search. |
| searchCacheIdent = ident; |
| searchCacheSymbol = s; |
| searchCacheFlags = flags; |
| } |
| return s; |
| } |
| |
| bool Module::isPackageAccessible(Package *p, Prot protection, int flags) |
| { |
| if (insearch) // don't follow import cycles |
| return false; |
| if (flags & IgnorePrivateImports) |
| protection = Prot(PROTpublic); // only consider public imports |
| insearch = true; |
| bool r = ScopeDsymbol::isPackageAccessible(p, protection); |
| insearch = false; |
| return r; |
| } |
| |
| Dsymbol *Module::symtabInsert(Dsymbol *s) |
| { |
| searchCacheIdent = NULL; // 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); |
| } |
| |
| void Module::addDeferredSemantic2(Dsymbol *s) |
| { |
| //printf("Module::addDeferredSemantic2('%s')\n", s->toChars()); |
| deferred2.push(s); |
| } |
| |
| void Module::addDeferredSemantic3(Dsymbol *s) |
| { |
| //printf("Module::addDeferredSemantic3('%s')\n", s->toChars()); |
| deferred3.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(), len = %d\n", deferred.dim); |
| nested++; |
| |
| size_t len; |
| do |
| { |
| dprogress = 0; |
| len = deferred.dim; |
| if (!len) |
| break; |
| |
| Dsymbol **todo; |
| Dsymbol **todoalloc = NULL; |
| Dsymbol *tmp; |
| if (len == 1) |
| { |
| todo = &tmp; |
| } |
| else |
| { |
| todo = (Dsymbol **)mem.xmalloc(len * sizeof(Dsymbol *)); |
| todoalloc = 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); |
| if (todoalloc) |
| free(todoalloc); |
| } while (deferred.dim < len || dprogress); // while making progress |
| nested--; |
| //printf("-Module::runDeferredSemantic(), len = %d\n", deferred.dim); |
| } |
| |
| void Module::runDeferredSemantic2() |
| { |
| Module::runDeferredSemantic(); |
| |
| Dsymbols *a = &Module::deferred2; |
| for (size_t i = 0; i < a->dim; i++) |
| { |
| Dsymbol *s = (*a)[i]; |
| //printf("[%d] %s semantic2a\n", i, s->toPrettyChars()); |
| s->semantic2(NULL); |
| |
| if (global.errors) |
| break; |
| } |
| a->setDim(0); |
| } |
| |
| void Module::runDeferredSemantic3() |
| { |
| Module::runDeferredSemantic2(); |
| |
| Dsymbols *a = &Module::deferred3; |
| for (size_t i = 0; i < a->dim; i++) |
| { |
| Dsymbol *s = (*a)[i]; |
| //printf("[%d] %s semantic3a\n", i, s->toPrettyChars()); |
| |
| s->semantic3(NULL); |
| |
| if (global.errors) |
| break; |
| } |
| a->setDim(0); |
| } |
| |
| /************************************ |
| * 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()); |
| 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 true if module imports itself. |
| */ |
| |
| bool Module::selfImports() |
| { |
| //printf("Module::selfImports() %s\n", toChars()); |
| if (selfimports == 0) |
| { |
| for (size_t i = 0; i < amodules.dim; i++) |
| amodules[i]->insearch = 0; |
| |
| selfimports = imports(this) + 1; |
| |
| for (size_t i = 0; i < amodules.dim; i++) |
| amodules[i]->insearch = 0; |
| } |
| return selfimports == 2; |
| } |
| |
| /************************************* |
| * Return true if module imports root module. |
| */ |
| |
| bool Module::rootImports() |
| { |
| //printf("Module::rootImports() %s\n", toChars()); |
| if (rootimports == 0) |
| { |
| for (size_t i = 0; i < amodules.dim; i++) |
| amodules[i]->insearch = 0; |
| |
| rootimports = 1; |
| for (size_t i = 0; i < amodules.dim; ++i) |
| { |
| Module *m = amodules[i]; |
| if (m->isRoot() && imports(m)) |
| { |
| rootimports = 2; |
| break; |
| } |
| } |
| |
| for (size_t i = 0; i < amodules.dim; i++) |
| amodules[i]->insearch = 0; |
| } |
| return rootimports == 2; |
| } |
| |
| bool Module::isCoreModule(Identifier *ident) |
| { |
| return this->ident == ident && parent && parent->ident == Id::core && !parent->parent; |
| } |
| |
| /* =========================== ModuleDeclaration ===================== */ |
| |
| ModuleDeclaration::ModuleDeclaration(Loc loc, Identifiers *packages, Identifier *id) |
| { |
| this->loc = loc; |
| this->packages = packages; |
| this->id = id; |
| this->isdeprecated = false; |
| this->msg = NULL; |
| } |
| |
| const 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()); |
| return buf.extractString(); |
| } |
| |
| /* =========================== Package ===================== */ |
| |
| Package::Package(Identifier *ident) |
| : ScopeDsymbol(ident) |
| { |
| this->isPkgMod = PKGunknown; |
| this->mod = NULL; |
| static unsigned packageTag = 0; |
| this->tag = packageTag++; |
| } |
| |
| |
| const char *Package::kind() const |
| { |
| return "package"; |
| } |
| |
| Module *Package::isPackageMod() |
| { |
| if (isPkgMod == PKGmodule) |
| { |
| return mod; |
| } |
| return NULL; |
| } |
| |
| /** |
| * Checks if pkg is a sub-package of this |
| * |
| * For example, if this qualifies to 'a1.a2' and pkg - to 'a1.a2.a3', |
| * this function returns 'true'. If it is other way around or qualified |
| * package paths conflict function returns 'false'. |
| * |
| * Params: |
| * pkg = possible subpackage |
| * |
| * Returns: |
| * see description |
| */ |
| bool Package::isAncestorPackageOf(const Package * const pkg) const |
| { |
| if (this == pkg) |
| return true; |
| if (!pkg || !pkg->parent) |
| return false; |
| return isAncestorPackageOf(pkg->parent->isPackage()); |
| } |
| |
| void Package::semantic(Scope *) |
| { |
| if (semanticRun < PASSsemanticdone) |
| semanticRun = PASSsemanticdone; |
| } |
| |
| /**************************************************** |
| * Input: |
| * packages[] the pkg1.pkg2 of pkg1.pkg2.mod |
| * Returns: |
| * the symbol table that mod should be inserted into |
| * Output: |
| * *pparent the rightmost package, i.e. pkg2, or NULL if no packages |
| * *ppkg the leftmost package, i.e. pkg1, or NULL if no packages |
| */ |
| |
| 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]; |
| Package *pkg; |
| Dsymbol *p = dst->lookup(pid); |
| if (!p) |
| { |
| pkg = new Package(pid); |
| dst->insert(pkg); |
| pkg->parent = parent; |
| pkg->symtab = new DsymbolTable(); |
| } |
| else |
| { |
| pkg = p->isPackage(); |
| assert(pkg); |
| // 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 |
| |
| // But we still need a symbol table for it |
| if (!pkg->symtab) |
| pkg->symtab = new DsymbolTable(); |
| } |
| parent = pkg; |
| dst = pkg->symtab; |
| if (ppkg && !*ppkg) |
| *ppkg = pkg; |
| if (pkg->isModule()) |
| { |
| // Return the module so that a nice error message can be generated |
| if (ppkg) |
| *ppkg = (Package *)p; |
| break; |
| } |
| } |
| } |
| if (pparent) |
| *pparent = parent; |
| return dst; |
| } |
| |
| Dsymbol *Package::search(const Loc &loc, Identifier *ident, int flags) |
| { |
| //printf("%s Package::search('%s', flags = x%x)\n", toChars(), ident->toChars(), flags); |
| flags &= ~SearchLocalsOnly; // searching an import is always transitive |
| if (!isModule() && mod) |
| { |
| // Prefer full package name. |
| Dsymbol *s = symtab ? symtab->lookup(ident) : NULL; |
| if (s) |
| return s; |
| //printf("[%s] through pkdmod: %s\n", loc.toChars(), toChars()); |
| return mod->search(loc, ident, flags); |
| } |
| |
| return ScopeDsymbol::search(loc, ident, flags); |
| } |
| |
| /* =========================== ===================== */ |
| |
| /******************************************** |
| * Look for the source file if it's different from filename. |
| * Look for .di, .d, directory, and along global.path. |
| * Does not open the file. |
| * Output: |
| * path the path where the file was found if it was not the current directory |
| * Input: |
| * filename as supplied by the user |
| * global.path |
| * Returns: |
| * NULL if it's not different from filename. |
| */ |
| |
| const char *lookForSourceFile(const char **path, const char *filename) |
| { |
| /* Search along global.path for .di file, then .d file. |
| */ |
| *path = NULL; |
| |
| const char *sdi = FileName::forceExt(filename, global.hdr_ext); |
| if (FileName::exists(sdi) == 1) |
| return sdi; |
| |
| const char *sd = FileName::forceExt(filename, global.mars_ext); |
| if (FileName::exists(sd) == 1) |
| return sd; |
| |
| if (FileName::exists(filename) == 2) |
| { |
| /* The filename exists and it's a directory. |
| * Therefore, the result should be: filename/package.d |
| * iff filename/package.d is a file |
| */ |
| const char *ni = FileName::combine(filename, "package.di"); |
| if (FileName::exists(ni) == 1) |
| return ni; |
| FileName::free(ni); |
| const char *n = FileName::combine(filename, "package.d"); |
| if (FileName::exists(n) == 1) |
| return n; |
| FileName::free(n); |
| } |
| |
| if (FileName::absolute(filename)) |
| return NULL; |
| |
| if (!global.path) |
| return NULL; |
| |
| for (size_t i = 0; i < global.path->dim; i++) |
| { |
| const char *p = (*global.path)[i]; |
| |
| const char *n = FileName::combine(p, sdi); |
| if (FileName::exists(n) == 1) |
| { |
| *path = p; |
| return n; |
| } |
| FileName::free(n); |
| |
| n = FileName::combine(p, sd); |
| if (FileName::exists(n) == 1) |
| { |
| *path = p; |
| return n; |
| } |
| FileName::free(n); |
| |
| const char *b = FileName::removeExt(filename); |
| n = FileName::combine(p, b); |
| FileName::free(b); |
| if (FileName::exists(n) == 2) |
| { |
| const char *n2i = FileName::combine(n, "package.di"); |
| if (FileName::exists(n2i) == 1) |
| return n2i; |
| FileName::free(n2i); |
| const char *n2 = FileName::combine(n, "package.d"); |
| if (FileName::exists(n2) == 1) |
| { |
| *path = p; |
| return n2; |
| } |
| FileName::free(n2); |
| } |
| FileName::free(n); |
| } |
| return NULL; |
| } |