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