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