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