racket/collects/scribblings/reference/stx-taints.scrbl
2011-06-29 19:15:48 -06:00

196 lines
8.1 KiB
Racket

#lang scribble/doc
@(require "mz.rkt")
@title[#:tag "stxcerts"]{Syntax Taints}
@guideintro["stx-certs"]{syntax taints}
The @deftech{tamper status} of a syntax object is either
@tech{tainted}, @tech{armed}, or clean:
@itemlist[
@item{A @deftech{tainted} identifier is rejected by the macro expander
for use as either a binding or expression. If a syntax object
is @tech{tainted}, then any syntax object in the result of
@racket[(syntax-e _stx)] is @tech{tainted}, and
@racket[datum->syntax] with @racket[_stx] as its first argument
produces a @tech{tainted} syntax object.
Other derived operations, such as pattern matching in
@racket[syntax-case], also taint syntax objects when extracting
them from a @tech{tainted} syntax object.}
@item{An @deftech{armed} syntax object has a set of @deftech{dye packs},
which creates taints if the armed syntax object is used without
first @tech{disarm}ing the dye packs. In particular, if a
syntax object is @tech{armed}, @racket[syntax-e],
@racket[datum->syntax], @racket[quote-syntax], and derived
operations effectively treat the syntax object as
@tech{tainted}. The macro expander, in contrast,
@tech{disarms} dye packs before pulling apart syntax
objects.
Each @tech{dye pack}, which is added to a syntax object with
the @racket[syntax-arm] function, is keyed by an
@tech{inspector}. A dye pack can be @deftech{disarm}ed using
@racket[syntax-disarm] with an inspector that is the same as or
a superior of the dye pack's inspector.}
@item{A @defterm{clean} syntax object has no immediate taints or dye
packs, although it may contain syntax objects that are
@tech{tainted} or @tech{armed}.}
]
Taints cannot be removed, and attempting to arm a syntax object that
is already tainted has no effect on the resulting syntax object.
The macro expander @tech{disarm}s any syntax object that it encounters
in an expression position or as a module body. A syntax object is
therefore disarmed when it is provided to a @tech{syntax
transformer}. The transformer's result, however, is @deftech{rearm}ed
by copying to it any @tech{dye packs} that were originally attached to
the transformer's input. The @tech{rearm}ing process
@elemtag['(explain "taint-mode")]{obeys} the following rules:
@itemize[
@item{If the result has a @indexed-racket['taint-mode] property (see
@secref["stxprops"]) that is @indexed-racket['opaque], then
dye packs are attached to the immediate syntax object.}
@item{If the result has a @indexed-racket['taint-mode] property that
is @indexed-racket['none], then no dye pack is attached to
the syntax object. The @racket['none] mode is rarely
appropriate.}
@item{If the result has a @racket['taint-mode] property that is
@indexed-racket['transparent], then the dye packs are
propagated recursively to syntax object that corresponds to
elements of the syntax object's datum as a list (or, more
precisely, to the @racket[car]s of the datum as reached by
any number of @racket[cdr]s), and the immediate syntax
object loses its lexical context; If the immediate syntax
object is already @tech{armed}, then recursive propagation
taints the elements. Recursive propagation uses syntax
properties and shapes, as for the immediate
@tech{rearm}ing.}
@item{If the result has a @racket['taint-mode] property that is
@indexed-racket['transparent-binding], then dye packs are
attached in a way similar to @racket['transparent], but
further treating the syntax object corresponding to the
second list element as having a @racket['transparent] value
for the @racket['taint-mode] property if it does not already
have a @racket['taint-mode] property value.}
@item{If the result has no @racket['taint-mode] property value, but
its datum is a pair, and if the syntax object corresponding
to the @racket[car] of the pair is an identifier bound to
@racket[begin], @racket[module], or
@racket[#%plain-module-begin], then dye packs are propagated
as if the syntax object had the @racket['transparent]
property value.}
@item{If the result has no @racket['taint-mode] property value, but
its datum is a pair, and if the syntax object corresponding
to the @racket[car] of the pair is an identifier bound to
@racket[define-values] or @racket[define-syntaxes], then dye
packs are propagated as if the syntax object had the
@racket['transparent-binding] property value.}
]
For backward compatibility, a @indexed-racket['certify-mode] property
is treated the same as a @racket['taint-mode] property if the former
is not attached. To avoid accidental transfer of a
@racket['taint-mode] or @racket['certify-mode] property value, the
expander always removes any @racket['taint-mode] and
@racket['certify-mode] property on a syntax object that is passed to a
@tech{syntax transformer}.
@defproc[(syntax-tainted? [stx syntax?]) boolean?]{
Returns @racket[#t] if @racket[stx] is @tech{tainted}, @racket[#f]
otherwise.}
@defproc[(syntax-arm [stx syntax?]
[inspector (or/c inspector? #f) #f]
[use-mode? any/c #f])
syntax?]{
Produces a syntax object like @racket[stx], but @tech{armed} with a
@tech{dye pack} that is keyed by @racket[inspector].
A @racket[#f] value for @racket[inspector] is equivalent to an
inspector that depends on the current dynamic context:
@itemlist[
@item{when applying a syntax transformer is being applied, the
declaration-time code inspector of the module in which a syntax
transformer was bound;}
@item{when a module is being visited, the module's declaration-time
code inspector;}
@item{@racket[(current-code-inspector)], otherwise.}
]
If @racket[use-mode?] is @racket[#f], then if @racket[stx] is
@tech{tainted} or already armed with the key @racket[inspector], the
result is @racket[stx].
If @racket[use-mode?] is a true value, then a @tech{dye pack} is
not necessarily added directly to @racket[stx]. Instead, the @tech{dye pack}
is pushed to interior syntax objects in the same way that the
expander pushes armings into a syntax transformer's results when
@tech{rearm}ing (based on a @racket['taint-mode] @tech{syntax
property} or identifier bindings); see @elemref['(explain
"taint-mode")]{the expander's rearming rules} for more information. To
the degree that pushing dye packs into a syntax object must destructure
@racket[stx], existing taints or dye packs can lead to @tech{tainted}
results rather than @tech{armed} results.}
@defproc[(syntax-protect [stx syntax?]) syntax?]{
Equivalent to @racket[(syntax-arm stx #f #f #t)].}
@defproc[(syntax-disarm [stx syntax?]
[inspector (or/c inspector? #f)])
syntax?]{
Produces a @tech{disarm}ed version of @racket[stx], removing any
immediate @tech{dye packs} that match @racket[inspector]. An inspector
matches when it is either the same as or a super-inspector of the dye
pack's inspector. A @racket[#f] value for @racket[inspector] is
replaced by a specific inspector in the same way as for
@racket[syntax-arm].}
@defproc[(syntax-rearm [stx syntax?]
[from-stx syntax?]
[use-mode? any/c #f])
syntax?]{
Produces a @tech{rearm}ed or @tech{tainted} version of @racket[stx] by
adding all immediate taints and @tech{dye packs} of @racket[from-stx].
If @racket[use-mode?] is a true value, @racket[stx] is not necessarily
@tech{tainted} or @tech{armed} directly. Instead, taints or @tech{dye
packs} are pushed to interior syntax objects in the same way as for
@racket[syntax-arm] or @elemref['(explain "taint-mode")]{rearming by
the expander}.}
@defproc[(syntax-taint [stx syntax?]) syntax?]{
Returns @tech{tainted} version of @racket[stx]---equivalent to
@racket[(datum->syntax (syntax-arm stx) (syntax-e stx) stx stx)]---or
@racket[stx] if it is already tainted.}