src/Packages.cpp
author David Anderson <dvander@alliedmods.net>
Tue Jan 01 20:06:25 2013 -0800 (2013-01-01)
changeset 253 544c478362c2
parent 252 1dee2330aa78
child 256 3c184218462d
permissions -rw-r--r--
Add WIN32 define for MSVC builds.
     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     if (!pkg->contents_->append(zone, obj))
    73         return false;
    74 
    75     return true;
    76 }
    77 
    78 Importable *
    79 Package::import(Handle<String> name)
    80 {
    81     if (!contents_)
    82         return NULL;
    83     
    84     for (unsigned i = 0; i < contents_->length(); i++) {
    85         Importable *obj = contents_->at(i);
    86         if (obj->isPackage() && Package::cast(obj)->name() == name)
    87             return obj;
    88         if (obj->isModule() && Module::cast(obj)->name() == name)
    89             return obj;
    90     }
    91 
    92     return NULL;
    93 }
    94 
    95 PackageCache::PackageCache(Zone *zone)
    96   : zone_(zone)
    97 {
    98 }
    99 
   100 bool
   101 PackageCache::initialize()
   102 {
   103     return true;
   104 }
   105 
   106 void
   107 PackageCache::accept(VirtualObjectVisitor *visitor)
   108 {
   109     for (size_t i = 0; i < roots_.length(); i++)
   110         visitor->visit(roots_[i].address());
   111 }
   112 
   113 bool
   114 PackageCache::checkfs(Handle<Package> package, PathComponent *path, Importable **out)
   115 {
   116     AutoFree<char> left(strdup(package->path()->chars()));
   117     AutoFree<char> right(strdup(path->name->chars()));
   118     AutoFree<char> result;
   119 
   120     if (!JoinPaths(zone_, result, *left, *right))
   121         return false;
   122 
   123     OS::PathType type = OS::InspectPath(*result);
   124     if (type == OS::Path_Folder) {
   125         // If there is a folder matching this path, create a new subpackage,
   126         // and then either return it (if this is the last link), or keep
   127         // trying to import the rest of the path.
   128         Local<String> strpath(zone_, zone_->makeSymbol(*result));
   129         Local<Package> subpackage(zone_, Package::NewNormalized(zone_, package, strpath, path->name, Package::Normal));
   130         if (!subpackage)
   131             return false;
   132         if (!path->next) {
   133             *out = subpackage;
   134             return true;
   135         }
   136         return import(subpackage, path->next, out);
   137     }
   138 
   139     // Otherwise, look for a file ending in ".sp".
   140     AutoFree<char> filepath((char *)zone_->malloc(strlen(*result) + strlen(".sp") + 1));
   141     if (!*filepath)
   142         return false;
   143 
   144     strcpy(*filepath, *result);
   145     strcat(*filepath, ".sp");
   146 
   147     type = OS::InspectPath(*filepath);
   148     if (type != OS::Path_File)
   149         return true;
   150 
   151     // Create a new, empty module.
   152     Local<String> strpath(zone_, zone_->makeSymbol(*filepath));
   153     if (!strpath)
   154         return false;
   155 
   156     Local<Module> module(zone_, Module::New(zone_, package, strpath, path->name));
   157     if (!module || !package->add(zone_, module))
   158         return false;
   159 
   160     *out = module;
   161     return true;
   162 }
   163 
   164 bool
   165 PackageCache::import(Handle<Package> package, PathComponent *path, Importable **out)
   166 {
   167     // If the package is jailed, none of its imports are visible.
   168     if (package->jailed())
   169         return true;
   170 
   171     Local<Importable> obj(zone_, package->import(path->name));
   172     if (!obj) {
   173         // We couldn't find anything. Try the file system.
   174         if (!checkfs(package, path, out))
   175             return false;
   176 
   177         // checkfs will keep trying to import, so if it fails, we just
   178         // propagate up.
   179         return true;
   180     }
   181 
   182     // If we found an existing module, or the path has no next link, then
   183     // we can't go any further.
   184     if (obj->is(MapKind_Module) || !path->next) {
   185         *out = obj;
   186         return true;
   187     }
   188 
   189     Local<Package> subpackage(zone_, Package::cast(obj));
   190     return import(subpackage, path->next, out);
   191 }
   192 
   193 bool
   194 PackageCache::findOrAdd(Handle<Package> package, PathComponent *path, Importable **out)
   195 {
   196     // Local rooters should have initialized this, but whatever.
   197     assert(*out == NULL);
   198 
   199     // Try to import locally first.
   200     if (!import(package, path, out))
   201         return false;
   202     if (*out)
   203         return true;
   204 
   205     // Otherwise, search system roots.
   206     Local<Package> root(zone_);
   207     for (size_t i = 0; i < roots_.length(); i++) {
   208         root = roots_[i];
   209         if (!import(root, path, out))
   210             return false;
   211         if (*out)
   212             return true;
   213     }
   214 
   215     return true;
   216 }
   217 
   218 PackageCache::~PackageCache()
   219 {
   220 }