Implement |from x import *| syntax for modules.
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/.
21 #include "../Interpreter.h"
22 #include "../Packages.h"
23 #include "../FileSystem.h"
24 #include "../FixedArray.h"
25 #include "../Vector.h"
27 #include "CompileContext.h"
29 #include "NameBinding.h"
30 #include "SemanticAnalysis.h"
31 #include "../Spaces-inl.h"
32 #include "../Heap-inl.h"
36 CompileContext::CompileContext(Zone *zone, const char *file)
43 CompileContext::~CompileContext()
45 for (size_t i = 0; i < units_.length(); i++)
50 CompileContext::initialize(Handle<Package> package, const char *name)
52 size_t len = strlen(name);
53 AutoFree<char> path((char *)zone_->malloc(len + 1));
58 if (len > 3 && strcmp(&name[len - 3], ".sp") == 0)
59 (*path)[len - 3] = '\0';
61 Local<String> strname(zone_, zone_->makeSymbol(*path));
65 PathComponent p(new (zone_->pool()) ScopedRoot<String>(strname), NULL);
67 Local<Importable> importable(zone_);
68 if (!zone_->packages()->findOrAdd(package, &p, importable.address()))
71 // If we couldn't find anything on the filesystem to match, we error out
74 zone_->reportError(Message_CannotImportPath, *path);
78 if (importable->isModule()) {
79 Local<Module> module(zone_, Module::cast(importable));
91 CompileContext::loadSource(Handle<Module> module, size_t *lengthp)
96 FILE *fp = fopen(module->path()->chars(), "rb");
98 reportError(SourcePosition(), Message_CantOpenFile);
102 if (fseek(fp, 0, SEEK_END)) {
103 reportError(SourcePosition(), Message_ErrorSeekingFile);
107 long size = ftell(fp);
109 reportError(SourcePosition(), Message_ErrorSeekingFile);
114 length = size_t(size);
115 bytes = new char[length + 1];
116 if (fread(bytes, 1, length, fp) != length) {
117 reportError(SourcePosition(), Message_ErrorReadingFile);
129 CompileContext::parse(TranslationUnit *unit)
131 assert(!unit->tree_);
134 char *source = loadSource(unit->module(), &length);
138 Parser parser(ZONE(), *this, source, length);
139 if ((unit->tree_ = parser.parse()) == NULL)
146 CompileContext::evaluateImports(TranslationUnit *unit)
148 PoolList<ImportStatement *> *imports = unit->tree()->imports();
150 if (!imports->length())
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;
157 for (size_t i = 0; i < imports->length(); i++) {
158 ImportStatement *import = imports->at(i);
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()))
166 PathComponent *path = import->path();
169 reportError(import->pos(), Message_CannotImportPath, path->name->chars());
173 // If the object is a package, we will attempt to execute it
174 // immediately after resolving imports.
175 if (obj->isPackage()) {
178 Local<Module> module(zone_, Module::cast(obj));
183 import->setSource(obj);
185 // See if there are any duplicates. This is O(n^2), but we expect
188 for (; index < vec.length(); index++) {
189 if (vec[index] == obj)
192 if (index == vec.length() && !vec.append(import->source()))
195 import->setImportIndex(index);
198 if (!IsUint32MultiplySafe(vec.length(), 2)) {
199 zone_->reportAllocationOverflow();
203 // Create an array of the import and paths.
204 Local<FixedArray> importables(zone_, FixedArray::New(zone_, vec.length() * 2, Heap::Tenure_Default));
208 for (unsigned i = 0; i < vec.length(); i++) {
209 importables->set(zone_, i * 2, vec[i]);
212 PathComponent *path = imports->at(i)->path();
213 for (PathComponent *iter = path; iter; iter = iter->next)
216 Local<FixedArray> list(zone_, FixedArray::New(zone_, count, Heap::Tenure_Default));
221 for (PathComponent *iter = path; iter; iter = iter->next, count++)
222 list->set(zone_, count, iter->name);
224 importables->set(zone_, i * 2 + 1, list);
227 unit->module()->setImports(importables);
232 CompileContext::add(Handle<Module> module)
234 if (module->compiled())
237 TranslationUnit *tu = new (zone_->pool()) TranslationUnit(module);
239 if (!unparsed_.append(tu) || !units_.append(tu))
246 CompileContext::propagateFailure()
248 Vector<TranslationUnit *> worklist;
249 for (size_t i = 0; i < units_.length(); i++) {
250 if (units_[i]->failed() && !worklist.append(units_[i]))
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);
261 other->setDependencyFailed();
262 if (!worklist.append(other))
271 CompileContext::compile()
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();
278 AutoEnterCompileContext enter(*this, unit);
282 evaluateImports(unit);
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];
292 // Mark the module as currently in translation.
293 unit->module()->setTranslationUnit(unit);
295 PopulateNamesAndTypes(zone_, *this, unit);
298 // Phase 3. Complete imports and bind all names.
299 for (size_t i = 0; i < units_.length(); i++) {
300 TranslationUnit *unit = units_[i];
304 BindGlobals(zone_, *this, unit);
309 BindNamesAndTypes(zone_, *this, unit);
312 // Intermediate phase. Decide which modules we should not perform
313 // semantic analysis on.
314 if (!propagateFailure())
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];
323 AutoEnterCompileContext enter(*this, unit);
324 SemanticAnalysis sema(zone_, *this, unit);
328 // Phase 5. Run modules.
329 for (size_t i = 0; i < units_.length(); i++) {
330 TranslationUnit *unit = units_[i];
332 // Mark the unit as no longer being translated.
333 unit->module()->setTranslationUnit(NULL);
339 if (!zone_->stack().pushModuleFrame(unit->module_, args))
343 if (!Interpret(zone_, NULL, &slot))
347 if (Ke::IMessageReporter *reporter = zone_->errorReporter()) {
348 for (size_t i = 0; i < units_.length(); i++) {
349 TranslationUnit *unit = units_[i];
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())
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);
361 reporter->endCompilationErrors();
365 return !units_[0]->failed();
369 CompileContext::enter(TranslationUnit *unit)
376 CompileContext::leave()
383 CompileContext::reportErrorVa(const SourcePosition &pos, Message msg, va_list ap)
385 const char *report = BuildErrorMessage(Messages[msg].format, ap);
387 active_->setOutOfMemory();
391 active_->appendError(report, pos.line, pos.col);
395 CompileContext::reportError(const SourcePosition &pos, Message msg, ...)
399 reportErrorVa(pos, msg, ap);
404 TranslationUnit::destroy()
406 for (size_t i = 0; i < errors_.length(); i++)
407 FreeErrorMessage(errors_[i].message);