View source with formatted comments or as raw
    1/*  Part of SWI-Prolog
    2
    3    Author:        Jan Wielemaker
    4    E-mail:        J.Wielemaker@vu.nl
    5    WWW:           http://www.swi-prolog.org
    6    Copyright (c)  2014-2015, VU University Amsterdam
    7    All rights reserved.
    8
    9    Redistribution and use in source and binary forms, with or without
   10    modification, are permitted provided that the following conditions
   11    are met:
   12
   13    1. Redistributions of source code must retain the above copyright
   14       notice, this list of conditions and the following disclaimer.
   15
   16    2. Redistributions in binary form must reproduce the above copyright
   17       notice, this list of conditions and the following disclaimer in
   18       the documentation and/or other materials provided with the
   19       distribution.
   20
   21    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   22    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   23    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
   24    FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
   25    COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
   26    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   27    BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   28    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   29    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   30    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
   31    ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   32    POSSIBILITY OF SUCH DAMAGE.
   33*/
   34
   35:- module(modules,
   36          [ in_temporary_module/3               % ?Module, :Setup, :Goal
   37          ]).   38
   39
   40/** <module> Module utility predicates
   41*/
   42
   43:- meta_predicate
   44    in_temporary_module(?, 0, 0).   45
   46%!  in_temporary_module(?Module, :Setup, :Goal)
   47%
   48%   Run Goal on temporary loaded sources  and discard the module and
   49%   loaded predicates after completion.  This predicate performs the
   50%   following steps:
   51%
   52%     1. If Module is unbound, create a unique identifier for it.
   53%     2. Turn Module into a _temporary_ module using set_module/1.
   54%        Note that this requires the module to be non-existent or
   55%        empty.  If Module is specified, it should typically be set
   56%        to a unique value as obtained from e.g. uuid/1.
   57%     3. Run Setup in the context of Module.
   58%     4. If setup succeeded possible choice points are discarded
   59%        and Goal is started.
   60%
   61%   The  logical  result  of  this   predicate    is   the  same  as
   62%   `(Setup@Module -> Goal@Module)`, i.e., both   Setup and Goal are
   63%   resolved relative to the current  module,   but  executed in the
   64%   context of Module.  If  Goal  must   be  called  in  Module, use
   65%   `call(Goal)`.
   66%
   67%   The module and all  its  predicates   are  destroyed  after Goal
   68%   terminates, as defined by setup_call_cleanup/3.
   69%
   70%   *Discussion* This predicate is intended to   load programs in an
   71%   isolated   environment   and   reclaim   all   resources.   This
   72%   unfortunately is incomplete:
   73%
   74%     - Running the code may leave side effects such as creating
   75%       records, flags, changing Prolog flags, etc.  The system
   76%       has no provisions to track this.
   77%     - So called _functors_ (name/arity pairs) are not yet subject
   78%       to garbage collection.  Functors are both used to define
   79%       predicates and to create compound terms.
   80%
   81%   @see    library(sandbox) determines whether unknown goals are safe
   82%           to call.
   83%   @see    load_files/2 offers the option sandboxed(true) to load code
   84%           from unknown sources safely.
   85
   86in_temporary_module(Module, Setup, Goal) :-
   87    setup_call_cleanup(
   88        prepare_temporary_module(Module),
   89        (   @(Setup, Module)
   90        ->  @(Goal,  Module)
   91        ),
   92        destroy_module(Module)).
   93
   94prepare_temporary_module(Module) :-
   95    var(Module),
   96    !,
   97    (   thread_id(Tid),
   98        repeat,
   99        I is random(1<<62),
  100        atomic_list_concat([tmp, Tid, I], -, Module),
  101        catch(set_module(Module:class(temporary)),
  102              error(permission_error(_,_,_),_), fail)
  103    ->  true
  104    ).
  105prepare_temporary_module(Module) :-
  106    set_module(Module:class(temporary)).
  107
  108:- if(current_prolog_flag(threads, true)).  109thread_id(Id) :-
  110    thread_self(Self),
  111    thread_property(Self, id(Id)).
  112:- else.  113thread_id(main).
  114:- endif.  115
  116destroy_module(Module) :-
  117    retractall(system:'$load_context_module'(_File, Module, _Options)),
  118    '$destroy_module'(Module)