src/compiler/CompileContext.cpp
author David Anderson <dvander@alliedmods.net>
Sun Jan 06 13:52:21 2013 -0800 (2013-01-06)
changeset 256 3c184218462d
parent 252 1dee2330aa78
child 258 241d082d6d89
permissions -rw-r--r--
Initial implementation of module imports.
     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 <string.h>
    20 #include "../Zone.h"
    21 #include "../Interpreter.h"
    22 #include "../Packages.h"
    23 #include "../FileSystem.h"
    24 #include "../FixedArray.h"
    25 #include "../Vector.h"
    26 #include "Scanner.h"
    27 #include "CompileContext.h"
    28 #include "Parser.h"
    29 #include "NameBinding.h"
    30 #include "SemanticAnalysis.h"
    31 #include "../Spaces-inl.h"
    32 #include "../Heap-inl.h"
    33 
    34 using namespace ke;
    35 
    36 CompileContext::CompileContext(Zone *zone, const char *file)
    37   : zone_(zone),
    38     file_(file),
    39     active_(NULL)
    40 {
    41 }
    42 
    43 CompileContext::~CompileContext()
    44 {
    45     for (size_t i = 0; i < units_.length(); i++)
    46         units_[i]->destroy();
    47 }
    48 
    49 bool
    50 CompileContext::initialize(Handle<Package> package, const char *name)
    51 {
    52     size_t len = strlen(name);
    53     AutoFree<char> path((char *)zone_->malloc(len + 1));
    54     if (!*path)
    55         return false;
    56 
    57     strcpy(*path, name);
    58     if (len > 3 && strcmp(&name[len - 3], ".sp") == 0)
    59         (*path)[len - 3] = '\0';
    60 
    61     Local<String> strname(zone_, zone_->makeSymbol(*path));
    62     if (!strname)
    63         return false;
    64 
    65     PathComponent p(new (zone_->pool()) ScopedRoot<String>(strname), NULL);
    66 
    67     Local<Importable> importable(zone_);
    68     if (!zone_->packages()->findOrAdd(package, &p, importable.address()))
    69         return false;
    70 
    71     // If we couldn't find anything on the filesystem to match, we error out
    72     // early.
    73     if (!importable) {
    74         zone_->reportError(Message_CannotImportPath, *path);
    75         return false;
    76     }
    77 
    78     if (importable->isModule()) {
    79         Local<Module> module(zone_, Module::cast(importable));
    80         if (!add(module))
    81             return true;
    82     } else {
    83         // nyi
    84         assert(false);
    85     }
    86 
    87     return true;
    88 }
    89 
    90 char *
    91 CompileContext::loadSource(Handle<Module> module, size_t *lengthp)
    92 {
    93     char *bytes = NULL;
    94     size_t length;
    95 
    96     FILE *fp = fopen(module->path()->chars(), "rb");
    97     if (!fp) {
    98         reportError(SourcePosition(), Message_CantOpenFile);
    99         return NULL;
   100     }
   101 
   102     if (fseek(fp, 0, SEEK_END)) {
   103         reportError(SourcePosition(), Message_ErrorSeekingFile);
   104         fclose(fp);
   105         return NULL;
   106     }
   107     long size = ftell(fp);
   108     if (size < 0) {
   109         reportError(SourcePosition(), Message_ErrorSeekingFile);
   110         fclose(fp);
   111         return NULL;
   112     }
   113     rewind(fp);
   114     length = size_t(size);
   115     bytes = new char[length + 1];
   116     if (fread(bytes, 1, length, fp) != length) {
   117         reportError(SourcePosition(), Message_ErrorReadingFile);
   118         fclose(fp);
   119         delete [] bytes;
   120         return NULL;
   121     }
   122     fclose(fp);
   123 
   124     *lengthp = length;
   125     return bytes;
   126 }
   127 
   128 bool
   129 CompileContext::parse(TranslationUnit *unit)
   130 {
   131     assert(!unit->tree_);
   132 
   133     size_t length;
   134     char *source = loadSource(unit->module(), &length);
   135     if (!source)
   136         return false;
   137 
   138     Parser parser(ZONE(), *this, source, length);
   139     if ((unit->tree_ = parser.parse()) == NULL)
   140         return false;
   141 
   142     return true;
   143 }
   144 
   145 bool
   146 CompileContext::evaluateImports(TranslationUnit *unit)
   147 {
   148     PoolList<ImportStatement *> *imports = unit->tree()->imports();
   149 
   150     if (!imports->length())
   151         return true;
   152 
   153     // We're allowed to use Handle<> here instead of ScopedRoot<> because
   154     // we get the handle from ImportStatement which has a ScopedRoot<>.
   155     Vector<Handle<Importable> > vec;
   156 
   157     for (size_t i = 0; i < imports->length(); i++) {
   158         ImportStatement *import = imports->at(i);
   159 
   160         // Crawl the path until we find a valid file to load.
   161         Local<Importable> obj(zone_);
   162         Local<Package> package(zone_, unit->module()->parent());
   163         if (!zone_->packages()->findOrAdd(package, import->path(), obj.address()))
   164             return false;
   165         if (!obj) {
   166             PathComponent *path = import->path();
   167             while (path->next)
   168                 path = path->next;
   169             reportError(import->pos(), Message_CannotImportPath, path->name->chars());
   170             return false;
   171         }
   172 
   173         // If the object is a package, we will attempt to execute it
   174         // immediately after resolving imports.
   175         if (obj->isPackage()) {
   176             assert(false);
   177         } else {
   178             Local<Module> module(zone_, Module::cast(obj));
   179             if (!add(module))
   180                 return false;
   181         }
   182 
   183         import->setSource(obj);
   184         
   185         // See if there are any duplicates. This is O(n^2), but we expect
   186         // |n| to be small.
   187         unsigned index = 0;
   188         for (; index < vec.length(); index++) {
   189             if (vec[index] == obj)
   190                 break;
   191         }
   192         if (index == vec.length() && !vec.append(import->source()))
   193             return false;
   194 
   195         import->setImportIndex(index);
   196     }
   197 
   198     if (!IsUint32MultiplySafe(vec.length(), 2)) {
   199         zone_->reportAllocationOverflow();
   200         return false;
   201     }
   202 
   203     // Create an array of the import and paths.
   204     Local<FixedArray> importables(zone_, FixedArray::New(zone_, vec.length() * 2, Heap::Tenure_Default));
   205     if (!importables)
   206         return false;
   207 
   208     for (unsigned i = 0; i < vec.length(); i++) {
   209         importables->set(zone_, i * 2, vec[i]);
   210 
   211         unsigned count = 0;
   212         PathComponent *path = imports->at(i)->path();
   213         for (PathComponent *iter = path; iter; iter = iter->next)
   214             count++;
   215 
   216         Local<FixedArray> list(zone_, FixedArray::New(zone_, count, Heap::Tenure_Default));
   217         if (!list)
   218             return false;
   219 
   220         count = 0;
   221         for (PathComponent *iter = path; iter; iter = iter->next, count++)
   222             list->set(zone_, count, iter->name);
   223 
   224         importables->set(zone_, i * 2 + 1, list);
   225     }
   226 
   227     unit->module()->setImports(importables);
   228     return true;
   229 }
   230 
   231 bool
   232 CompileContext::add(Handle<Module> module)
   233 {
   234     if (module->compiled())
   235         return true;
   236 
   237     TranslationUnit *tu = new (zone_->pool()) TranslationUnit(module);
   238 
   239     if (!unparsed_.append(tu) || !units_.append(tu))
   240         return false;
   241 
   242     return true;
   243 }
   244 
   245 bool
   246 CompileContext::propagateFailure()
   247 {
   248     Vector<TranslationUnit *> worklist;
   249     for (size_t i = 0; i < units_.length(); i++) {
   250         if (units_[i]->failed() && !worklist.append(units_[i]))
   251             return false;
   252     }
   253 
   254     while (!worklist.empty()) {
   255         TranslationUnit *unit = worklist.popCopy();
   256         for (size_t i = 0; i < unit->dependents().length(); i++) {
   257             TranslationUnit *other = unit->dependents().at(i);
   258             if (other->failed())
   259                 continue;
   260 
   261             other->setDependencyFailed();
   262             if (!worklist.append(other))
   263                 return false;
   264         }
   265     }
   266 
   267     return true;
   268 }
   269 
   270 bool
   271 CompileContext::compile()
   272 {
   273     // Phase 1. Parse all modules we're importing. This process may load new
   274     // modules off disk, and add them to the parse queue.
   275     while (!unparsed_.empty()) {
   276         TranslationUnit *unit = unparsed_.popCopy();
   277 
   278         AutoEnterCompileContext enter(*this, unit);
   279         if (!parse(unit))
   280             continue;
   281 
   282         evaluateImports(unit);
   283     }
   284 
   285     // Phase 2. Create scopes and symbol tables for every lexical region with
   286     // a declaration. This phase excludes declarations created via imports.
   287     for (size_t i = 0; i < units_.length(); i++) {
   288         TranslationUnit *unit = units_[i];
   289         if (unit->failed())
   290             continue;
   291 
   292         PopulateNamesAndTypes(zone_, *this, unit);
   293     }
   294 
   295     // Phase 3. Complete imports and bind all names.
   296     for (size_t i = 0; i < units_.length(); i++) {
   297         TranslationUnit *unit = units_[i];
   298         if (unit->failed())
   299             continue;
   300 
   301         BindNamesAndTypes(zone_, *this, unit);
   302     }
   303 
   304     // Phase 4. Assign types to every public name in the module.
   305     for (size_t i = 0; i < units_.length(); i++) {
   306         TranslationUnit *unit = units_[i];
   307         if (unit->failed())
   308             continue;
   309 
   310     }
   311 
   312     // Intermediate phase. Decide which modules we should not perform
   313     // semantic analysis on.
   314     if (!propagateFailure())
   315         return false;
   316 
   317     // Phase 4. Now we can finally perform SemA+bytecode compilation.
   318     for (size_t i = 0; i < units_.length(); i++) {
   319         TranslationUnit *unit = units_[i];
   320         if (unit->failed())
   321             continue;
   322 
   323         AutoEnterCompileContext enter(*this, unit);
   324         SemanticAnalysis sema(zone_, *this, unit);
   325         sema.analyze();
   326     }
   327 
   328     // Phase 5. Run modules.
   329     for (size_t i = 0; i < units_.length(); i++) {
   330         TranslationUnit *unit = units_[i];
   331         if (unit->failed())
   332             break;
   333 
   334         InvokeArgs args;
   335         if (!zone_->stack().pushModuleFrame(unit->module_, args))
   336             assert(false);
   337 
   338         ke::LocalSlot slot;
   339         if (!Interpret(zone_, NULL, &slot))
   340             assert(false);
   341     }
   342 
   343     if (Ke::IMessageReporter *reporter = zone_->errorReporter()) {
   344         for (size_t i = 0; i < units_.length(); i++) {
   345             TranslationUnit *unit = units_[i];
   346 
   347             // We don't explicitly report errors for dependency failures or OOM
   348             // (OOM is reported separately (:TODO:) and dep failures are redundant.
   349             if (!unit->errors()->length())
   350                 continue;
   351 
   352             reporter->beginCompilationErrors(unit->module()->path()->chars());
   353             for (size_t j = 0; j < unit->errors()->length(); j++) {
   354                 const CompileError &error = unit->errors()->at(j);
   355                 reporter->reportCompilationError(error.message, error.line, error.col);
   356             }
   357             reporter->endCompilationErrors();
   358         }
   359     }
   360 
   361     return !units_[0]->failed();
   362 }
   363 
   364 void
   365 CompileContext::enter(TranslationUnit *unit)
   366 {
   367     assert(!active_);
   368     active_ = unit;
   369 }
   370 
   371 void
   372 CompileContext::leave()
   373 {
   374     assert(active_);
   375     active_ = NULL;
   376 }
   377 
   378 void
   379 CompileContext::reportErrorVa(const SourcePosition &pos, Message msg, va_list ap)
   380 {
   381     const char *report = BuildErrorMessage(Messages[msg].format, ap);
   382     if (!report) {
   383         active_->setOutOfMemory();
   384         return;
   385     }
   386 
   387     active_->appendError(report, pos.line, pos.col);
   388 }
   389 
   390 void
   391 CompileContext::reportError(const SourcePosition &pos, Message msg, ...)
   392 {
   393     va_list ap;
   394     va_start(ap, msg);
   395     reportErrorVa(pos, msg, ap);
   396     va_end(ap);
   397 }
   398 
   399 void
   400 TranslationUnit::destroy()
   401 {
   402     for (size_t i = 0; i < errors_.length(); i++)
   403         FreeErrorMessage(errors_[i].message);
   404 }