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