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