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