src/compiler/CompileContext.cpp
author David Anderson <dvander@alliedmods.net>
Tue Jan 01 15:34:07 2013 -0800 (2013-01-01)
changeset 251 ca99a81745fe
parent 250 0998a43cc155
child 252 1dee2330aa78
permissions -rw-r--r--
Refactor compilation process around the new concept of packages.
     1 /*
     2  * Copyright (C) 2012 David Anderson
     3  *
     4  * This file is part of SourcePawn.
     5  *
     6  * SourcePawn is free software: you can redistribute it and/or modify it under
     7  * the terms of the GNU General Public License as published by the Free
     8  * Software Foundation, either version 3 of the License, or (at your option)
     9  * any later version.
    10  * 
    11  * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY
    12  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    13  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
    14  *
    15  * You should have received a copy of the GNU General Public License along with
    16  * SourcePawn. If not, see http://www.gnu.org/licenses/.
    17  */
    18 #include <stdio.h>
    19 #include "../Zone.h"
    20 #include "../Interpreter.h"
    21 #include "../Packages.h"
    22 #include "../FileSystem.h"
    23 #include "Scanner.h"
    24 #include "CompileContext.h"
    25 #include "Parser.h"
    26 #include "NameBinding.h"
    27 #include "SemanticAnalysis.h"
    28 #include "../Spaces-inl.h"
    29 #include "../Heap-inl.h"
    30 
    31 using namespace ke;
    32 
    33 CompileContext::CompileContext(Zone *zone, const char *file)
    34   : zone_(zone),
    35     file_(file),
    36     active_(NULL)
    37 {
    38 }
    39 
    40 CompileContext::~CompileContext()
    41 {
    42     for (size_t i = 0; i < units_.length(); i++)
    43         units_[i]->destroy();
    44 }
    45 
    46 bool
    47 CompileContext::initialize(Handle<Package> package, const char *name)
    48 {
    49     size_t len = strlen(name);
    50     AutoFree<char> path((char *)zone_->malloc(len + 1));
    51     if (!*path)
    52         return false;
    53 
    54     strcpy(*path, name);
    55     if (len > 3 && strcmp(&name[len - 3], ".sp") == 0)
    56         (*path)[len - 3] = '\0';
    57 
    58     Local<String> strname(zone_, zone_->makeSymbol(*path));
    59     if (!strname)
    60         return false;
    61 
    62     PathComponent p(new ScopedRoot<String>(strname), NULL);
    63 
    64     Local<Importable> importable(zone_);
    65     if (!zone_->packages()->findOrAdd(package, &p, importable.address()))
    66         return false;
    67 
    68     // If we couldn't find anything on the filesystem to match, we error out
    69     // early.
    70     if (!importable) {
    71         zone_->reportError(Message_CannotImportPath, *path);
    72         return false;
    73     }
    74 
    75     if (importable->isModule()) {
    76         Local<Module> module(zone_, Module::cast(importable));
    77         if (!add(module))
    78             return true;
    79     } else {
    80         // nyi
    81         assert(false);
    82     }
    83 
    84     return true;
    85 }
    86 
    87 char *
    88 CompileContext::loadSource(Handle<Module> module, size_t *lengthp)
    89 {
    90     char *bytes = NULL;
    91     size_t length;
    92 
    93     FILE *fp = fopen(module->path()->chars(), "rb");
    94     if (!fp) {
    95         reportError(SourcePosition(), Message_CantOpenFile);
    96         return NULL;
    97     }
    98 
    99     if (fseek(fp, 0, SEEK_END)) {
   100         reportError(SourcePosition(), Message_ErrorSeekingFile);
   101         fclose(fp);
   102         return NULL;
   103     }
   104     long size = ftell(fp);
   105     if (size < 0) {
   106         reportError(SourcePosition(), Message_ErrorSeekingFile);
   107         fclose(fp);
   108         return NULL;
   109     }
   110     rewind(fp);
   111     length = size_t(size);
   112     bytes = new char[length + 1];
   113     if (fread(bytes, 1, length, fp) != length) {
   114         reportError(SourcePosition(), Message_ErrorReadingFile);
   115         fclose(fp);
   116         delete [] bytes;
   117         return NULL;
   118     }
   119     fclose(fp);
   120 
   121     *lengthp = length;
   122     return bytes;
   123 }
   124 
   125 bool
   126 CompileContext::parse(TranslationUnit *unit)
   127 {
   128     assert(!unit->tree_);
   129 
   130     size_t length;
   131     char *source = loadSource(unit->module(), &length);
   132     if (!source)
   133         return false;
   134 
   135     Parser parser(ZONE(), *this, source, length);
   136     if ((unit->tree_ = parser.parse()) == NULL)
   137         return false;
   138 
   139     return true;
   140 }
   141 
   142 bool
   143 CompileContext::evaluateImports(TranslationUnit *unit)
   144 {
   145     PoolList<ImportStatement *> *imports = unit->tree()->imports();
   146 
   147     for (size_t i = 0; i < imports->length(); i++) {
   148         ImportStatement *import = imports->at(i);
   149 
   150         // Step 1. Crawl the path until we find a valid file to load.
   151         Local<Importable> obj(zone_);
   152         Local<Package> package(zone_, unit->module()->parent());
   153         if (!zone_->packages()->findOrAdd(package, import->path(), obj.address()))
   154             return false;
   155         if (!obj) {
   156             PathComponent *path = import->path();
   157             while (path->next)
   158                 path = path->next;
   159             reportError(import->pos(), Message_CannotImportPath, path->name->chars());
   160             return false;
   161         }
   162 
   163         // If the object is a package, we will attempt to execute it
   164         // immediately after resolving imports.
   165         if (obj->isPackage()) {
   166             assert(false);
   167         } else {
   168             Local<Module> module(zone_, Module::cast(obj));
   169             if (!add(module))
   170                 return false;
   171         }
   172     }
   173 
   174     return true;
   175 }
   176 
   177 bool
   178 CompileContext::add(Handle<Module> module)
   179 {
   180     if (module->compiled())
   181         return true;
   182 
   183     TranslationUnit *tu = new (zone_->pool()) TranslationUnit(module);
   184 
   185     if (!unparsed_.append(tu) || !units_.append(tu))
   186         return false;
   187 
   188     return true;
   189 }
   190 
   191 bool
   192 CompileContext::propagateFailure()
   193 {
   194     Vector<TranslationUnit *> worklist;
   195     for (size_t i = 0; i < units_.length(); i++) {
   196         if (units_[i]->failed() && !worklist.append(units_[i]))
   197             return false;
   198     }
   199 
   200     while (!worklist.empty()) {
   201         TranslationUnit *unit = worklist.popCopy();
   202         for (size_t i = 0; i < unit->dependents().length(); i++) {
   203             TranslationUnit *other = unit->dependents().at(i);
   204             if (other->failed())
   205                 continue;
   206 
   207             other->setDependencyFailed();
   208             if (!worklist.append(other))
   209                 return false;
   210         }
   211     }
   212 
   213     return true;
   214 }
   215 
   216 bool
   217 CompileContext::compile()
   218 {
   219     // Phase 1. Parse all modules we're importing. This process may load new
   220     // modules off disk, and add them to the parse queue.
   221     while (!unparsed_.empty()) {
   222         TranslationUnit *unit = unparsed_.popCopy();
   223 
   224         AutoEnterCompileContext enter(*this, unit);
   225         if (!parse(unit))
   226             continue;
   227 
   228         evaluateImports(unit);
   229     }
   230 
   231     // Phase 2. Create scopes and symbol tables for every lexical region with
   232     // a declaration. This phase excludes declarations created via imports.
   233     for (size_t i = 0; i < units_.length(); i++) {
   234         TranslationUnit *unit = units_[i];
   235         if (unit->failed())
   236             continue;
   237 
   238         PopulateNamesAndTypes(zone_, *this, unit);
   239     }
   240 
   241     // Phase 3. Complete imports and bind all names.
   242     for (size_t i = 0; i < units_.length(); i++) {
   243         TranslationUnit *unit = units_[i];
   244         if (unit->failed())
   245             continue;
   246 
   247         BindNamesAndTypes(zone_, *this, unit);
   248     }
   249 
   250     // Phase 4. Assign types to every public name in the module.
   251     for (size_t i = 0; i < units_.length(); i++) {
   252         TranslationUnit *unit = units_[i];
   253         if (unit->failed())
   254             continue;
   255 
   256     }
   257 
   258     // Intermediate phase. Decide which modules we should not perform
   259     // semantic analysis on.
   260     if (!propagateFailure())
   261         return false;
   262 
   263     // Phase 4. Now we can finally perform SemA+bytecode compilation.
   264     for (size_t i = 0; i < units_.length(); i++) {
   265         TranslationUnit *unit = units_[i];
   266         if (unit->failed())
   267             continue;
   268 
   269         AutoEnterCompileContext enter(*this, unit);
   270         SemanticAnalysis sema(zone_, *this, unit);
   271         sema.analyze();
   272     }
   273 
   274     // Phase 5. Run modules.
   275     for (size_t i = 0; i < units_.length(); i++) {
   276         TranslationUnit *unit = units_[i];
   277         if (unit->failed())
   278             break;
   279 
   280         InvokeArgs args;
   281         if (!zone_->stack().pushModuleFrame(unit->module_, args))
   282             assert(false);
   283 
   284         ke::LocalSlot slot;
   285         if (!Interpret(zone_, NULL, &slot))
   286             assert(false);
   287     }
   288 
   289     if (Ke::IMessageReporter *reporter = zone_->errorReporter()) {
   290         for (size_t i = 0; i < units_.length(); i++) {
   291             TranslationUnit *unit = units_[i];
   292 
   293             // We don't explicitly report errors for dependency failures or OOM
   294             // (OOM is reported separately (:TODO:) and dep failures are redundant.
   295             if (!unit->errors()->length())
   296                 continue;
   297 
   298             reporter->beginCompilationErrors(unit->module()->path()->chars());
   299             for (size_t j = 0; j < unit->errors()->length(); j++) {
   300                 const CompileError &error = unit->errors()->at(j);
   301                 reporter->reportCompilationError(error.message, error.line, error.col);
   302             }
   303             reporter->endCompilationErrors();
   304         }
   305     }
   306 
   307     return !units_[0]->failed();
   308 }
   309 
   310 void
   311 CompileContext::enter(TranslationUnit *unit)
   312 {
   313     assert(!active_);
   314     active_ = unit;
   315 }
   316 
   317 void
   318 CompileContext::leave()
   319 {
   320     assert(active_);
   321     active_ = NULL;
   322 }
   323 
   324 void
   325 CompileContext::reportErrorVa(const SourcePosition &pos, Message msg, va_list ap)
   326 {
   327     const char *report = BuildErrorMessage(Messages[msg].format, ap);
   328     if (!report) {
   329         active_->setOutOfMemory();
   330         return;
   331     }
   332 
   333     active_->appendError(report, pos.line, pos.col);
   334 }
   335 
   336 void
   337 CompileContext::reportError(const SourcePosition &pos, Message msg, ...)
   338 {
   339     va_list ap;
   340     va_start(ap, msg);
   341     reportErrorVa(pos, msg, ap);
   342     va_end(ap);
   343 }
   344 
   345 void
   346 TranslationUnit::destroy()
   347 {
   348     for (size_t i = 0; i < errors_.length(); i++)
   349         FreeErrorMessage(errors_[i].message);
   350 }