asherkin@263: /* asherkin@263: * Copyright (C) 2012 David Anderson asherkin@263: * asherkin@263: * This file is part of SourcePawn. asherkin@263: * asherkin@263: * SourcePawn is free software: you can redistribute it and/or modify it under asherkin@263: * the terms of the GNU General Public License as published by the Free asherkin@263: * Software Foundation, either version 3 of the License, or (at your option) asherkin@263: * any later version. asherkin@263: * asherkin@263: * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY asherkin@263: * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS asherkin@263: * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. asherkin@263: * asherkin@263: * You should have received a copy of the GNU General Public License along with asherkin@263: * SourcePawn. If not, see http://www.gnu.org/licenses/. asherkin@263: */ asherkin@263: #include asherkin@263: #include "Zone.h" asherkin@263: #include "Heap.h" asherkin@263: #include "Strings.h" asherkin@263: #include "Modules.h" asherkin@263: #include "Packages.h" asherkin@263: #include "FileSystem.h" asherkin@263: #include "List.h" asherkin@263: #include "Heap-inl.h" asherkin@263: #include "Handles-inl.h" asherkin@263: asherkin@263: using namespace ke; asherkin@263: asherkin@263: Package * asherkin@263: Package::NewNormalized(Zone *zone, Handle parent, Handle path, Handle name, unsigned flags) asherkin@263: { asherkin@263: Local package(zone, Package::cast(zone->allocate(MapKind_Package, sizeof(Package), Heap::Tenure_Old))); asherkin@263: if (!package) asherkin@263: return NULL; asherkin@263: asherkin@263: // :TODO: assert path, name are atoms. asherkin@263: asherkin@263: package->flags_ = flags; asherkin@263: package->name_ = name; asherkin@263: package->parent_ = parent; asherkin@263: package->path_ = path; asherkin@263: package->nativeIdentity_ = parent ? parent->getNativeIdentity() : NULL; asherkin@263: asherkin@263: return package; asherkin@263: } asherkin@263: asherkin@263: Package * asherkin@263: Package::New(Zone *zone, Handle parent, const char *path, Handle name, unsigned flags) asherkin@263: { asherkin@263: Local strpath(zone, zone->makeSymbol(path)); asherkin@263: if (!strpath) asherkin@263: return NULL; asherkin@263: asherkin@263: return Package::NewNormalized(zone, parent, strpath, name, flags); asherkin@263: } asherkin@263: asherkin@263: bool asherkin@263: Package::add(Zone *zone, Handle obj) asherkin@263: { asherkin@263: Local pkg(zone, this); asherkin@263: asherkin@263: assert(obj->parent() == pkg); asherkin@263: asherkin@263: if (!pkg->contents_) { asherkin@263: pkg->contents_ = List::New(zone); asherkin@263: if (!pkg->contents_) asherkin@263: return false; asherkin@263: } asherkin@263: asherkin@263: #ifndef NDEBUG asherkin@263: // Assert no duplicate names. asherkin@263: for (unsigned i = 0; i < pkg->contents_->length(); i++) asherkin@263: assert(pkg->contents_->at(i)->name() != obj->name()); asherkin@263: #endif asherkin@263: asherkin@263: if (!pkg->contents_->append(zone, obj)) asherkin@263: return false; asherkin@263: asherkin@263: return true; asherkin@263: } asherkin@263: asherkin@263: Importable * asherkin@263: Package::import(Handle name) asherkin@263: { asherkin@263: if (!contents_) asherkin@263: return NULL; asherkin@263: asherkin@263: for (unsigned i = 0; i < contents_->length(); i++) { asherkin@263: Importable *obj = contents_->at(i); asherkin@263: if (obj->isPackage() && Package::cast(obj)->name() == name) asherkin@263: return obj; asherkin@263: if (obj->isModule() && Module::cast(obj)->name() == name) asherkin@263: return obj; asherkin@263: } asherkin@263: asherkin@263: return NULL; asherkin@263: } asherkin@263: asherkin@263: PackageCache::PackageCache(Zone *zone) asherkin@263: : zone_(zone) asherkin@263: { asherkin@263: } asherkin@263: asherkin@263: bool asherkin@263: PackageCache::initialize() asherkin@263: { asherkin@263: return true; asherkin@263: } asherkin@263: asherkin@263: void asherkin@263: PackageCache::accept(VirtualObjectVisitor *visitor) asherkin@263: { asherkin@263: for (size_t i = 0; i < roots_.length(); i++) asherkin@263: visitor->visit(roots_[i].address()); asherkin@263: } asherkin@263: asherkin@263: bool asherkin@263: PackageCache::checkfs(Handle package, PathComponent *path, Importable **out) asherkin@263: { asherkin@263: AutoFree left(strdup(package->path()->chars())); asherkin@263: AutoFree right(strdup(path->name->chars())); asherkin@263: AutoFree result; asherkin@263: asherkin@263: if (!JoinPaths(zone_, result, *left, *right)) asherkin@263: return false; asherkin@263: asherkin@263: OS::PathType type = OS::InspectPath(*result); asherkin@263: if (type == OS::Path_Folder) { asherkin@263: // If there is a folder matching this path, create a new subpackage, asherkin@263: // and then either return it (if this is the last link), or keep asherkin@263: // trying to import the rest of the path. asherkin@263: Local strpath(zone_, zone_->makeSymbol(*result)); asherkin@263: Local subpackage(zone_, Package::NewNormalized(zone_, package, strpath, path->name, Package::Normal)); asherkin@263: if (!subpackage) asherkin@263: return false; asherkin@263: if (!path->next) { asherkin@263: *out = subpackage; asherkin@263: return true; asherkin@263: } asherkin@263: return import(subpackage, path->next, out); asherkin@263: } asherkin@263: asherkin@263: // Otherwise, look for a file ending in ".sp". asherkin@263: AutoFree filepath((char *)zone_->malloc(strlen(*result) + strlen(".sp") + 1)); asherkin@263: if (!*filepath) asherkin@263: return false; asherkin@263: asherkin@263: strcpy(*filepath, *result); asherkin@263: strcat(*filepath, ".sp"); asherkin@263: asherkin@263: type = OS::InspectPath(*filepath); asherkin@263: if (type != OS::Path_File) asherkin@263: return true; asherkin@263: asherkin@263: // Create a new, empty module. asherkin@263: Local strpath(zone_, zone_->makeSymbol(*filepath)); asherkin@263: if (!strpath) asherkin@263: return false; asherkin@263: asherkin@263: Local module(zone_, Module::New(zone_, package, strpath, path->name)); asherkin@263: if (!module || !package->add(zone_, module)) asherkin@263: return false; asherkin@263: asherkin@263: *out = module; asherkin@263: return true; asherkin@263: } asherkin@263: asherkin@263: bool asherkin@263: PackageCache::import(Handle package, PathComponent *path, Importable **out) asherkin@263: { asherkin@263: // If the package is jailed, none of its imports are visible. asherkin@263: if (package->jailed()) asherkin@263: return true; asherkin@263: asherkin@263: Local obj(zone_, package->import(path->name)); asherkin@263: if (!obj) { asherkin@263: // We couldn't find anything. Try the file system. asherkin@263: if (!checkfs(package, path, out)) asherkin@263: return false; asherkin@263: asherkin@263: // checkfs will keep trying to import, so if it fails, we just asherkin@263: // propagate up. asherkin@263: return true; asherkin@263: } asherkin@263: asherkin@263: // If we found an existing module, or the path has no next link, then asherkin@263: // we can't go any further. asherkin@263: if (obj->is(MapKind_Module) || !path->next) { asherkin@263: *out = obj; asherkin@263: return true; asherkin@263: } asherkin@263: asherkin@263: Local subpackage(zone_, Package::cast(obj)); asherkin@263: return import(subpackage, path->next, out); asherkin@263: } asherkin@263: asherkin@263: bool asherkin@263: PackageCache::findOrAdd(Handle package, PathComponent *path, Importable **out) asherkin@263: { asherkin@263: // Local rooters should have initialized this, but whatever. asherkin@263: assert(*out == NULL); asherkin@263: asherkin@263: // Try to import locally first. asherkin@263: if (!import(package, path, out)) asherkin@263: return false; asherkin@263: if (*out) asherkin@263: return true; asherkin@263: asherkin@263: // Otherwise, search system roots. asherkin@263: Local root(zone_); asherkin@263: for (size_t i = 0; i < roots_.length(); i++) { asherkin@263: root = roots_[i]; asherkin@263: if (!import(root, path, out)) asherkin@263: return false; asherkin@263: if (*out) asherkin@263: return true; asherkin@263: } asherkin@263: asherkin@263: return true; asherkin@263: } asherkin@263: asherkin@263: PackageCache::~PackageCache() asherkin@263: { asherkin@263: }