You want to
eval code that:
- Doesn't have access to anything declared or loaded by the code that calls
eval
- Does have access to certain modules you choose to allow it access to
And all this without the code you are evaluating having to include
require statements to load the modules it needs.
All code is evaluated within a
namespace. You must firstly create a new empty
namespace for the code you wish to
eval, and then attach any necessary modules to it:
(define new-namespace (make-namespace 'initial))
(parameterize ((current-namespace new-namespace))
(namespace-require '(file "my-module.ss"))
(eval untrusted-code))
Typically code evaluated in
eval has access to anything visible to the code that calls
eval. E.g.:
> (define (foo x) (/ x 2))
> (eval '(foo 2))
1
This is insecure -- the evaluated code could mess with the internals of the code that evaluates it. [TODO]
This is useful, for example, when web pages. Say you have a library of code that you use to write webpages (e.g.
SSAX or
WebIt and some transformations to HTML). You want to write the webpages as Scheme programs but you want to put each page in its own file and you don't want to worry about all the usual
module wrapper code to load your library. You can use this method to create a custom namespace that includes your library and
eval the code in this namespace.
[TODO: talk about attaching modules from existing namespace]
See also
DynamicUntrustedEval
Here's an
eval that hides the
set! form:
(define (my-eval sxp)
(define owner-ns (current-namespace))
(parameterize ([current-namespace (make-namespace 'empty)])
(namespace-attach-module owner-ns 'mzscheme)
(namespace-require '(all-except mzscheme set!))
(eval sxp)))
So if you try to bang on an identifier, you get an error:
> (my-eval '(begin (define x 2) (set! x 3) x))
reference to undefined identifier: set!
Note that
namespace-attach-module only registers an instantiated module in the module registry of a destination namespace. It doesn't actually import any symbols as the (require ...) form does -- for that you need to
namespace-require the registered module.
To instantiate a module that you haven't required in the source namespace, use dynamic-require. For example, if you have a module
foo in foo.ss:
(module foo mzscheme
(printf "Instantiated foo!~n"))
To provide foo.ss to your users, change your
my-eval to instantiate it:
(define (my-eval2 sxp)
(define owner-ns (current-namespace))
(define foo-module-path ((current-module-name-resolver) "foo.ss"
#f #f))
(dynamic-require bah-module-path #f)
(parameterize ([current-namespace (make-namespace 'empty)])
(namespace-attach-module owner-ns 'mzscheme)
(namespace-require '(all-except mzscheme set!))
(namespace-attach-module owner-ns foo-module-path)
(namespace-require '"foo.ss")
(eval sxp)))
Remember that modules are only instantiated once per namespace, so you'll only see foo's load-time output one time:
> (my-eval2 '1)
Instantiated foo!
1
> (my-eval2 '1)
1
--
DanielSilva - 25 Jan 2005
--
NoelWelsh - 20 Jan 2005