src/Packages.cpp
author David Anderson <dvander@alliedmods.net>
Sun Jan 06 13:52:21 2013 -0800 (2013-01-06)
changeset 256 3c184218462d
parent 252 1dee2330aa78
child 263 ba85a47ee414
permissions -rw-r--r--
Initial implementation of module imports.
     1 /*
     2  * Copyright (C) 2012 David Anderson
     3  *
     4  * This file is part of SourcePawn.
     5  *
     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)
     9  * any later version.
    10  * 
    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.
    14  *
    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/.
    17  */
    18 #include <string.h>
    19 #include "Zone.h"
    20 #include "Heap.h"
    21 #include "Strings.h"
    22 #include "Modules.h"
    23 #include "Packages.h"
    24 #include "FileSystem.h"
    25 #include "List.h"
    26 #include "Heap-inl.h"
    27 #include "Handles-inl.h"
    28 
    29 using namespace ke;
    30 
    31 Package *
    32 Package::NewNormalized(Zone *zone, Handle<Package> parent, Handle<String> path, Handle<String> name, unsigned flags)
    33 {
    34     Local<Package> package(zone, Package::cast(zone->allocate(MapKind_Package, sizeof(Package), Heap::Tenure_Old)));
    35     if (!package)
    36         return NULL;
    37 
    38     // :TODO: assert path, name are atoms.
    39 
    40     package->flags_ = flags;
    41     package->name_ = name;
    42     package->parent_ = parent;
    43     package->path_ = path;
    44     package->nativeIdentity_ = parent ? parent->getNativeIdentity() : NULL;
    45 
    46     return package;
    47 }
    48 
    49 Package *
    50 Package::New(Zone *zone, Handle<Package> parent, const char *path, Handle<String> name, unsigned flags)
    51 {
    52     Local<String> strpath(zone, zone->makeSymbol(path));
    53     if (!strpath)
    54         return NULL;
    55 
    56     return Package::NewNormalized(zone, parent, strpath, name, flags);
    57 }
    58 
    59 bool
    60 Package::add(Zone *zone, Handle<Importable> obj)
    61 {
    62     Local<Package> pkg(zone, this);
    63 
    64     assert(obj->parent() == pkg);
    65 
    66     if (!pkg->contents_) {
    67         pkg->contents_ = List<Importable>::New(zone);
    68         if (!pkg->contents_)
    69             return false;
    70     }
    71 
    72 #ifndef NDEBUG
    73     // Assert no duplicate names.
    74     for (unsigned i = 0; i < pkg->contents_->length(); i++)
    75         assert(pkg->contents_->at(i)->name() != obj->name());
    76 #endif
    77 
    78     if (!pkg->contents_->append(zone, obj))
    79         return false;
    80 
    81     return true;
    82 }
    83 
    84 Importable *
    85 Package::import(Handle<String> name)
    86 {
    87     if (!contents_)
    88         return NULL;
    89     
    90     for (unsigned i = 0; i < contents_->length(); i++) {
    91         Importable *obj = contents_->at(i);
    92         if (obj->isPackage() && Package::cast(obj)->name() == name)
    93             return obj;
    94         if (obj->isModule() && Module::cast(obj)->name() == name)
    95             return obj;
    96     }
    97 
    98     return NULL;
    99 }
   100 
   101 PackageCache::PackageCache(Zone *zone)
   102   : zone_(zone)
   103 {
   104 }
   105 
   106 bool
   107 PackageCache::initialize()
   108 {
   109     return true;
   110 }
   111 
   112 void
   113 PackageCache::accept(VirtualObjectVisitor *visitor)
   114 {
   115     for (size_t i = 0; i < roots_.length(); i++)
   116         visitor->visit(roots_[i].address());
   117 }
   118 
   119 bool
   120 PackageCache::checkfs(Handle<Package> package, PathComponent *path, Importable **out)
   121 {
   122     AutoFree<char> left(strdup(package->path()->chars()));
   123     AutoFree<char> right(strdup(path->name->chars()));
   124     AutoFree<char> result;
   125 
   126     if (!JoinPaths(zone_, result, *left, *right))
   127         return false;
   128 
   129     OS::PathType type = OS::InspectPath(*result);
   130     if (type == OS::Path_Folder) {
   131         // If there is a folder matching this path, create a new subpackage,
   132         // and then either return it (if this is the last link), or keep
   133         // trying to import the rest of the path.
   134         Local<String> strpath(zone_, zone_->makeSymbol(*result));
   135         Local<Package> subpackage(zone_, Package::NewNormalized(zone_, package, strpath, path->name, Package::Normal));
   136         if (!subpackage)
   137             return false;
   138         if (!path->next) {
   139             *out = subpackage;
   140             return true;
   141         }
   142         return import(subpackage, path->next, out);
   143     }
   144 
   145     // Otherwise, look for a file ending in ".sp".
   146     AutoFree<char> filepath((char *)zone_->malloc(strlen(*result) + strlen(".sp") + 1));
   147     if (!*filepath)
   148         return false;
   149 
   150     strcpy(*filepath, *result);
   151     strcat(*filepath, ".sp");
   152 
   153     type = OS::InspectPath(*filepath);
   154     if (type != OS::Path_File)
   155         return true;
   156 
   157     // Create a new, empty module.
   158     Local<String> strpath(zone_, zone_->makeSymbol(*filepath));
   159     if (!strpath)
   160         return false;
   161 
   162     Local<Module> module(zone_, Module::New(zone_, package, strpath, path->name));
   163     if (!module || !package->add(zone_, module))
   164         return false;
   165 
   166     *out = module;
   167     return true;
   168 }
   169 
   170 bool
   171 PackageCache::import(Handle<Package> package, PathComponent *path, Importable **out)
   172 {
   173     // If the package is jailed, none of its imports are visible.
   174     if (package->jailed())
   175         return true;
   176 
   177     Local<Importable> obj(zone_, package->import(path->name));
   178     if (!obj) {
   179         // We couldn't find anything. Try the file system.
   180         if (!checkfs(package, path, out))
   181             return false;
   182 
   183         // checkfs will keep trying to import, so if it fails, we just
   184         // propagate up.
   185         return true;
   186     }
   187 
   188     // If we found an existing module, or the path has no next link, then
   189     // we can't go any further.
   190     if (obj->is(MapKind_Module) || !path->next) {
   191         *out = obj;
   192         return true;
   193     }
   194 
   195     Local<Package> subpackage(zone_, Package::cast(obj));
   196     return import(subpackage, path->next, out);
   197 }
   198 
   199 bool
   200 PackageCache::findOrAdd(Handle<Package> package, PathComponent *path, Importable **out)
   201 {
   202     // Local rooters should have initialized this, but whatever.
   203     assert(*out == NULL);
   204 
   205     // Try to import locally first.
   206     if (!import(package, path, out))
   207         return false;
   208     if (*out)
   209         return true;
   210 
   211     // Otherwise, search system roots.
   212     Local<Package> root(zone_);
   213     for (size_t i = 0; i < roots_.length(); i++) {
   214         root = roots_[i];
   215         if (!import(root, path, out))
   216             return false;
   217         if (*out)
   218             return true;
   219     }
   220 
   221     return true;
   222 }
   223 
   224 PackageCache::~PackageCache()
   225 {
   226 }