src/compiler/CompileContext.cpp
author David Anderson <dvander@alliedmods.net>
Sat Jan 12 16:13:46 2013 -0800 (2013-01-12)
changeset 258 241d082d6d89
parent 256 3c184218462d
child 260 05de34f712fe
permissions -rw-r--r--
Implement |from x import *| syntax for modules.
     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         // Mark the module as currently in translation.
   293         unit->module()->setTranslationUnit(unit);
   294 
   295         PopulateNamesAndTypes(zone_, *this, unit);
   296     }
   297 
   298     // Phase 3. Complete imports and bind all names.
   299     for (size_t i = 0; i < units_.length(); i++) {
   300         TranslationUnit *unit = units_[i];
   301         if (unit->failed())
   302             continue;
   303 
   304         BindGlobals(zone_, *this, unit);
   305 
   306         if (unit->failed())
   307             continue;
   308 
   309         BindNamesAndTypes(zone_, *this, unit);
   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 
   332         // Mark the unit as no longer being translated.
   333         unit->module()->setTranslationUnit(NULL);
   334 
   335         if (unit->failed())
   336             break;
   337 
   338         InvokeArgs args;
   339         if (!zone_->stack().pushModuleFrame(unit->module_, args))
   340             assert(false);
   341 
   342         ke::LocalSlot slot;
   343         if (!Interpret(zone_, NULL, &slot))
   344             assert(false);
   345     }
   346 
   347     if (Ke::IMessageReporter *reporter = zone_->errorReporter()) {
   348         for (size_t i = 0; i < units_.length(); i++) {
   349             TranslationUnit *unit = units_[i];
   350 
   351             // We don't explicitly report errors for dependency failures or OOM
   352             // (OOM is reported separately (:TODO:) and dep failures are redundant.
   353             if (!unit->errors()->length())
   354                 continue;
   355 
   356             reporter->beginCompilationErrors(unit->module()->path()->chars());
   357             for (size_t j = 0; j < unit->errors()->length(); j++) {
   358                 const CompileError &error = unit->errors()->at(j);
   359                 reporter->reportCompilationError(error.message, error.line, error.col);
   360             }
   361             reporter->endCompilationErrors();
   362         }
   363     }
   364 
   365     return !units_[0]->failed();
   366 }
   367 
   368 void
   369 CompileContext::enter(TranslationUnit *unit)
   370 {
   371     assert(!active_);
   372     active_ = unit;
   373 }
   374 
   375 void
   376 CompileContext::leave()
   377 {
   378     assert(active_);
   379     active_ = NULL;
   380 }
   381 
   382 void
   383 CompileContext::reportErrorVa(const SourcePosition &pos, Message msg, va_list ap)
   384 {
   385     const char *report = BuildErrorMessage(Messages[msg].format, ap);
   386     if (!report) {
   387         active_->setOutOfMemory();
   388         return;
   389     }
   390 
   391     active_->appendError(report, pos.line, pos.col);
   392 }
   393 
   394 void
   395 CompileContext::reportError(const SourcePosition &pos, Message msg, ...)
   396 {
   397     va_list ap;
   398     va_start(ap, msg);
   399     reportErrorVa(pos, msg, ap);
   400     va_end(ap);
   401 }
   402 
   403 void
   404 TranslationUnit::destroy()
   405 {
   406     for (size_t i = 0; i < errors_.length(); i++)
   407         FreeErrorMessage(errors_[i].message);
   408 }