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