Refactor compilation process around the new concept of packages.
2 * Copyright (C) 2012 David Anderson
4 * This file is part of SourcePawn.
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)
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.
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/.
20 #include "../Interpreter.h"
21 #include "../Packages.h"
22 #include "../FileSystem.h"
24 #include "CompileContext.h"
26 #include "NameBinding.h"
27 #include "SemanticAnalysis.h"
28 #include "../Spaces-inl.h"
29 #include "../Heap-inl.h"
33 CompileContext::CompileContext(Zone *zone, const char *file)
40 CompileContext::~CompileContext()
42 for (size_t i = 0; i < units_.length(); i++)
47 CompileContext::initialize(Handle<Package> package, const char *name)
49 size_t len = strlen(name);
50 AutoFree<char> path((char *)zone_->malloc(len + 1));
55 if (len > 3 && strcmp(&name[len - 3], ".sp") == 0)
56 (*path)[len - 3] = '\0';
58 Local<String> strname(zone_, zone_->makeSymbol(*path));
62 PathComponent p(new ScopedRoot<String>(strname), NULL);
64 Local<Importable> importable(zone_);
65 if (!zone_->packages()->findOrAdd(package, &p, importable.address()))
68 // If we couldn't find anything on the filesystem to match, we error out
71 zone_->reportError(Message_CannotImportPath, *path);
75 if (importable->isModule()) {
76 Local<Module> module(zone_, Module::cast(importable));
88 CompileContext::loadSource(Handle<Module> module, size_t *lengthp)
93 FILE *fp = fopen(module->path()->chars(), "rb");
95 reportError(SourcePosition(), Message_CantOpenFile);
99 if (fseek(fp, 0, SEEK_END)) {
100 reportError(SourcePosition(), Message_ErrorSeekingFile);
104 long size = ftell(fp);
106 reportError(SourcePosition(), Message_ErrorSeekingFile);
111 length = size_t(size);
112 bytes = new char[length + 1];
113 if (fread(bytes, 1, length, fp) != length) {
114 reportError(SourcePosition(), Message_ErrorReadingFile);
126 CompileContext::parse(TranslationUnit *unit)
128 assert(!unit->tree_);
131 char *source = loadSource(unit->module(), &length);
135 Parser parser(ZONE(), *this, source, length);
136 if ((unit->tree_ = parser.parse()) == NULL)
143 CompileContext::evaluateImports(TranslationUnit *unit)
145 PoolList<ImportStatement *> *imports = unit->tree()->imports();
147 for (size_t i = 0; i < imports->length(); i++) {
148 ImportStatement *import = imports->at(i);
150 // Step 1. Crawl the path until we find a valid file to load.
151 Local<Importable> obj(zone_);
152 Local<Package> package(zone_, unit->module()->parent());
153 if (!zone_->packages()->findOrAdd(package, import->path(), obj.address()))
156 PathComponent *path = import->path();
159 reportError(import->pos(), Message_CannotImportPath, path->name->chars());
163 // If the object is a package, we will attempt to execute it
164 // immediately after resolving imports.
165 if (obj->isPackage()) {
168 Local<Module> module(zone_, Module::cast(obj));
178 CompileContext::add(Handle<Module> module)
180 if (module->compiled())
183 TranslationUnit *tu = new (zone_->pool()) TranslationUnit(module);
185 if (!unparsed_.append(tu) || !units_.append(tu))
192 CompileContext::propagateFailure()
194 Vector<TranslationUnit *> worklist;
195 for (size_t i = 0; i < units_.length(); i++) {
196 if (units_[i]->failed() && !worklist.append(units_[i]))
200 while (!worklist.empty()) {
201 TranslationUnit *unit = worklist.popCopy();
202 for (size_t i = 0; i < unit->dependents().length(); i++) {
203 TranslationUnit *other = unit->dependents().at(i);
207 other->setDependencyFailed();
208 if (!worklist.append(other))
217 CompileContext::compile()
219 // Phase 1. Parse all modules we're importing. This process may load new
220 // modules off disk, and add them to the parse queue.
221 while (!unparsed_.empty()) {
222 TranslationUnit *unit = unparsed_.popCopy();
224 AutoEnterCompileContext enter(*this, unit);
228 evaluateImports(unit);
231 // Phase 2. Create scopes and symbol tables for every lexical region with
232 // a declaration. This phase excludes declarations created via imports.
233 for (size_t i = 0; i < units_.length(); i++) {
234 TranslationUnit *unit = units_[i];
238 PopulateNamesAndTypes(zone_, *this, unit);
241 // Phase 3. Complete imports and bind all names.
242 for (size_t i = 0; i < units_.length(); i++) {
243 TranslationUnit *unit = units_[i];
247 BindNamesAndTypes(zone_, *this, unit);
250 // Phase 4. Assign types to every public name in the module.
251 for (size_t i = 0; i < units_.length(); i++) {
252 TranslationUnit *unit = units_[i];
258 // Intermediate phase. Decide which modules we should not perform
259 // semantic analysis on.
260 if (!propagateFailure())
263 // Phase 4. Now we can finally perform SemA+bytecode compilation.
264 for (size_t i = 0; i < units_.length(); i++) {
265 TranslationUnit *unit = units_[i];
269 AutoEnterCompileContext enter(*this, unit);
270 SemanticAnalysis sema(zone_, *this, unit);
274 // Phase 5. Run modules.
275 for (size_t i = 0; i < units_.length(); i++) {
276 TranslationUnit *unit = units_[i];
281 if (!zone_->stack().pushModuleFrame(unit->module_, args))
285 if (!Interpret(zone_, NULL, &slot))
289 if (Ke::IMessageReporter *reporter = zone_->errorReporter()) {
290 for (size_t i = 0; i < units_.length(); i++) {
291 TranslationUnit *unit = units_[i];
293 // We don't explicitly report errors for dependency failures or OOM
294 // (OOM is reported separately (:TODO:) and dep failures are redundant.
295 if (!unit->errors()->length())
298 reporter->beginCompilationErrors(unit->module()->path()->chars());
299 for (size_t j = 0; j < unit->errors()->length(); j++) {
300 const CompileError &error = unit->errors()->at(j);
301 reporter->reportCompilationError(error.message, error.line, error.col);
303 reporter->endCompilationErrors();
307 return !units_[0]->failed();
311 CompileContext::enter(TranslationUnit *unit)
318 CompileContext::leave()
325 CompileContext::reportErrorVa(const SourcePosition &pos, Message msg, va_list ap)
327 const char *report = BuildErrorMessage(Messages[msg].format, ap);
329 active_->setOutOfMemory();
333 active_->appendError(report, pos.line, pos.col);
337 CompileContext::reportError(const SourcePosition &pos, Message msg, ...)
341 reportErrorVa(pos, msg, ap);
346 TranslationUnit::destroy()
348 for (size_t i = 0; i < errors_.length(); i++)
349 FreeErrorMessage(errors_[i].message);