| Commit message (Collapse) | Author | Age | Files | Lines |
| |
|
|
|
|
|
|
|
|
|
| |
We need to include the previously entered lines into the code
that we presentation compile.
Management of this state makes the interpret method non tail
recursive, so we could blow the default stack with a multi-line entry
of hundreds of lines. I think thats an acceptable limitation.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The old implementation is still avaiable under a flag, but we'll
remove it in due course.
Design goal:
- Push as much code in src/interactive as possible to enable reuse
outside of the REPL
- Don't entangle the REPL completion with JLine. The enclosed test
case drives the REPL and autocompletion programatically.
- Don't hard code UI choices, like how to render symbols or
how to filter candidates.
When completion is requested, we wrap the entered code into the
same "interpreter wrapper" synthetic code as is done for regular
execution. We then start a throwaway instance of the presentation
compiler, which takes this as its one and only source file, and
has a classpath formed from the REPL's classpath and the REPL's
output directory (by default, this is in memory).
We can then typecheck the tree, and find the position in the synthetic
source corresponding to the cursor location. This is enough to use
the new completion APIs in the presentation compiler to prepare
a list of candidates.
We go to extra lengths to allow completion of partially typed
identifiers that appear to be keywords, e.g `global.def` should offer
`definitions`.
Two secret handshakes are included; move the the end of the line,
type `// print<TAB>` and you'll see the post-typer tree.
`// typeAt 4 6<TAB>` shows the type of the range position within
the buffer.
The enclosed unit test exercises most of the new functionality.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Some extra synthetic code generated under this mode failed to escape
input before adding it to a literal string. It used to get away with
this most of the time by triple quoting the literal.
This commit reuses Scala string escaping logic buried in `Constant`
to do this properly. Actually, the proper approach would be to build
the synthetic code with trees and quasiquotes, and avoid the mess
of stringly-genererated code.
I threw in some defensive hygiene for the reference to `Nil` while
I was in the neighbourhood.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
User imports that reference Predef are relocated to the top of
the wrapping template so that they can hide implicits defined
in Predef.
Only one import from Predef is retained for special treatment.
This is simple and sane. The test shows that `import Predef._`
restores Predef implicits even if a user-defined term would
normally be in scope.
A smart `:import` command to turn off or quarantine imports explicitly
would allow fine-grained control.
|
|
|
|
|
|
|
|
| |
Can be specified by `-Dscala.repl.welcome=Greeting` or in properties
file. It takes the same format arguments as the prompt, viz, version,
Java version and JVM name.
It can be disabled by `-Dscala.repl.welcome` with no text.
|
|
|
|
|
|
|
|
|
|
| |
To make code in error messages line up with the original line of
code, templated code is indented by the width of the prompt.
Use the raw prompt (without ANSI escapes or newlines) to determine
the indentation.
Also, indent only once per line.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Spark has been shipping a forked version of our REPL for
sometime. We have been trying to fold the patches back into
the mainline so they can defork. This is the last outstanding
issue.
Consider this REPL session:
```
scala> val x = StdIn.readInt
scala> class A(a: Int)
scala> serializedAndExecuteRemotely {
() => new A(x)
}
```
As shown by the enclosed test, the REPL, even with the
Spark friendly option `-Yrepl-class-based`, will re-initialize
`x` on the remote system.
This test simulates this by running a REPL session, and then
deserializing the resulting closure into a fresh classloader
based on the class files generated by that session. Before this
patch, it printed "evaluating x" twice.
This is based on the Spark change described:
https://github.com/mesos/spark/pull/535#discussion_r3541925
A followup commit will avoid the `val lineN$read = ` part if we
import classes or type aliases only.
[Original commit from Prashant Sharma, test case from Jason Zaugg]
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
This commit corrects many typos found in scaladocs, comments and
documentation. It should reduce a bit number of PRs which fix one
typo.
There are no changes in the 'real' code except one corrected name of
a JUnit test method and some error messages in exceptions. In the case
of typos in other method or field names etc., I just skipped them.
Obviously this commit doesn't fix all existing typos. I just generated
in IntelliJ the list of potential typos and looked through it quickly.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
This commit contains some minor changes made by the way when
implementing flat classpath.
Sample JUnit test that shows that all pieces of JUnit infrastructure
work correctly now uses assert method form JUnit as it should do from
the beginning.
I removed commented out lines which were obvious to me. In the case
of less obvious commented out lines I added TODOs as someone should
look at such places some day and clean them up.
I removed also some unnecessary semicolons and unused imports.
Many string concatenations using + have been changed to string
interpolation.
There's removed unused, private walkIterator method from ZipArchive.
It seems that it was unused since this commit:
https://github.com/scala/scala/commit/9d4994b96c77d914687433586eb6d1f9e49c520f
However, I had to add an exception for the compatibility checker
because it was complaining about this change.
I made some trivial corrections/optimisations like use 'findClassFile'
method instead of 'findClass' in combination with 'binary' to find
the class file.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
This commit integrates with the compiler the whole flat classpath
representation build next to the recursive one as an alternative.
From now flat classpath really works and can be turned on. There's
added flag -YclasspathImpl with two options: recursive (the default
one) and flat.
It was needed to make the dynamic dispatch to the particular
classpath representation according to the chosen type of a classpath
representation.
There's added PathResolverFactory which is used instead of a concrete
implementation of a path resolver. It turned out that only a small
subset of path resolvers methods is used outside this class in Scala
sources. Therefore, PathResolverFactory returns an instance of a base
interface PathResolverResult providing only these used methods.
PathResolverFactory in combination with matches in some other places
ensures that in all places using classpath we create/get the proper
representation.
Also the classPath method in Global is modified to use the dynamic
dispatch. This is very important change as a return type changed to
the base ClassFileLookup providing subset of old ClassPath public
methods. It can be problematic if someone was using in his project
the explicit ClassPath type or public methods which are not provided
via ClassFileLookup. I tested flat classpath with sbt and Scala IDE
and there were no problems. Also was looking at sources of some other
projects like e.g. Scala plugin for IntelliJ and there shouldn't be
problems, I think, but it would be better to check these changes
using the community build.
Scalap's Main.scala is changed to be able to use both implementations
and also to use flags related to the classpath implementation.
The classpath invalidation is modified to work properly with the old
(recursive) classpath representation after changes made in a Global.
In the case of the attempt to use the invalidation for the flat cp it
just throws exception with a message that the flat one currently
doesn't support the invalidation. And also that's why the partest's
test for the invalidation has been changed to use (always) the old
implementation. There's added an adequate comment with TODO to this
file.
There's added partest test generating various dependencies
(directories, zips and jars with sources and class files) and testing
whether the compilation and further running an application works
correctly, when there are these various types of entries specified as
-classpath and -sourcepath. It should be a good approximation of real
use cases.
|
| |
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Fixes SI-6502, reenables loading jars into the running REPL
(regression in 2.10). This PR allows adding a jar to the compile
and runtime classpaths without resetting the REPL state (crucial
for Spark SPARK-3257).
This follows the lead taken by @som-snytt in PR #3986, which
differentiates two jar-loading behaviors (muddled by cp):
- adding jars and replaying REPL expressions (using replay)
- adding jars without resetting the REPL (deprecated cp,
introduced require) This PR implements require (left
unimplemented in #3986)
This PR is a simplification of a similar approach taken by
@gkossakowski in #3884. In this attempt, we check first to make
sure that a jar is only added if it only contains new
classes/traits/objects, otherwise we emit an error. This differs
from the old invalidation approach which also tracked deleted
classpath entries.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
To support both -Ydelambdafy strategies, look for both inline
(anonfun) and method (lambda) closure classes.
For method (lambda) style, use the anonfun method that is
invoked by the accessor.
Also, the output of javap must be captured eagerly for
filtering for the current target method.
If the user asks for a module, e.g., `Foo$`, don't yield
results for companion class, but for `Foo`, do yield
companion module results. Just because.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Let the AbstractFileClassLoader override just the usual suspects.
Normal delegation behavior should ensue.
That's instead of overriding `getResourceAsStream`, which was intended
that "The repl classloader now works more like you'd expect a classloader to."
(Workaround for "Don't know how to construct an URL for something which exists
only in memory.")
Also override `findResources` so that `getResources` does the obvious thing,
namely, return one iff `getResource` does.
The translating class loader for REPL only special-cases `foo.class`: as
a fallback, take `foo` as `$line42.$read$something$foo` and try that class file.
That's the use case for "works like you'd expect it to."
There was a previous fix to ensure `getResource` doesn't take a class name.
The convenience behavior, that `classBytes` takes either a class name or a resource
path ending in ".class", has been promoted to `ScalaClassLoader`.
|
|
|
|
|
|
| |
Create a trait Parsing, which, like Reporting,
factors our functionality from Global (aka. "the cake"),
that is related to global aspects of configuring parsing.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Move code from Global/SymbolTable to separate Reporting traits to
start carving out an interface in scala.reflect.internal.Reporting,
with internals in scala.tools.nsc. Reporting is mixed into the cake.
It contains a nested class PerRunReporting.
Should do the same for debugging/logging.
The idea is that CompilationUnit and Global forward all reporting
to Reporter. The Reporting trait contains these forwarders, and
PerRunReporting, which accumulates warning state during a run.
In the process, I slightly changed the behavior of `globalError`
in reflect.internal.SymbolTable: it used to abort, weirdly.
I assume that was dummy behavior to avoid introducing an abstract method.
It's immediately overridden in Global, and I couldn't find any other subclasses,
so I don't think the behavior in SymbolTable was ever observed.
Provide necessary hooks for scala.reflect.macros.Parsers#parse.
See scala/reflect/macros/contexts/Parsers.scala's parse method,
which overrides the reporter to detect when parsing goes wrong.
This should be refactored, but that goes beyond the scope of this PR.
Don't pop empty macro context stack.
(Ran into this while reworking -Xfatal-warnings logic.)
Fix -Xfatal-warnings behavior (and check files): it wasn't meant to
influence warning reporting, except for emitting one final error;
if necessary to fail the compile (when warnings but no errors were reported).
Warnings should stay warnings.
This was refactored in fbbbb22946, but we soon seem to have relapsed.
An hour of gitfu did not lead to where it went wrong. Must've been a merge.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Refactor to reduce the Reporter interface. Working towards
minimal interfaces in scala.reflect.internal that can be consumed by sbt/IDE/....
The scala.tools.nsc package is entirely private to the compiler (in principle).
A `Reporter` should only be used to inform (info/warning/error). No state.
Ideally, we'd move to having only one reporter, whose lifetime is adjusted
appropriately (from per-run in general to per-context for type checking,
so errors can be buffered -- "silenced" -- during nested type checking calls).
Start the clean up by moving truncation to the REPL,
since it's not relevant for regular reporting. Perversely, we were checking
truncation all the time, even though it's only on during a repl run.
(Truncation is now always turned off in the repl under -verbose.)
Untangle error resetting on symbols from error reporting (reportAdditionalErrors).
This fixes a nice&subtle bug that caused feature warnings to be suppressed under
`-Xfatal-warnings`:
```
def reportCompileErrors() {
if (!reporter.hasErrors && reporter.hasWarnings && settings.fatalWarnings)
globalError("No warnings can be incurred under -Xfatal-warnings.")
if (reporter.hasErrors) { ... }
else {
// will erroneously not get here if
// `reporter.hasWarnings && settings.fatalWarnings`
// since the `globalError` call above means `reporter.hasErrors`...
allConditionalWarnings foreach (_.summarize())
...
}
}
```
The second `if`'s condition depends on the `globalError` call in the first `if`...
|
|
|
|
|
|
|
|
| |
Incremental robustness, and probe for typer phase.
The probe would be unnecessary if repl contributed a
terminal phase that "requires" whatever it needs; that
is checked when the Run is built.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Under a weird coincidence of circumstances provided by `sbt console-quick`,
new XML parsing logic in compiler plugin initialization could lead to stack
overflow errors.
Here's the abridged sequence events that led to this unfortunate problem
(full description can be found on the JIRA issue page):
1) Initialization of the compiler underlying the REPL would kick off
plugin initialization.
2) PluginDescription.fromXML would call into DocumentBuilderFactory, i.e.
DocumentBuilderFactory.newInstance.newDocumentBuilder.parse(xml).
3) That thing would call into javax.xml.parsers.SecuritySupport.getResourceAsStream,
requesting META-INF/services/javax.xml.parsers.DocumentBuilderFactory.
4) That request would get serviced by TranslatingClassLoader provided
by the REPL to expose dynamically compiled code.
5) TranslatingClassLoader would call translatePath that would call into
IMain.symbolOfIdent trying to map the requested resource onto one of the
classes defined by the REPL (which don't exist yet, because REPL hasn't
yet finished initializing).
6) IMain.symbolOfIdent would request IMain.global, which is exactly the
instance of the compiler that underlies the REPL, and that's currently
being initialized.
7..inf) Repeat until a StackOverflowError.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Reflection API exhibits a tension inherent to experimental things:
on the one hand we want it to grow into a beautiful and robust API,
but on the other hand we have to deal with immaturity of underlying mechanisms
by providing not very pretty solutions to enable important use cases.
In Scala 2.10, which was our first stab at reflection API, we didn't
have a systematic approach to dealing with this tension, sometimes exposing
too much of internals (e.g. Symbol.deSkolemize) and sometimes exposing
too little (e.g. there's still no facility to change owners, to do typing
transformations, etc). This resulted in certain confusion with some internal
APIs living among public ones, scaring the newcomers, and some internal APIs
only available via casting, which requires intimate knowledge of the
compiler and breaks compatibility guarantees.
This led to creation of the `internal` API module for the reflection API,
which provides advanced APIs necessary for macros that push boundaries
of the state of the art, clearly demarcating them from the more or less
straightforward rest and providing compatibility guarantees on par with
the rest of the reflection API.
This commit does break source compatibility with reflection API in 2.10,
but the next commit is going to introduce a strategy of dealing with that.
|
|
|
|
|
|
|
| |
The problem is that the repl underneath the script engine evaluates input to
val res0..resN, so it is a one shot operation. To allow repetition,
compile(script) now returns a CompiledScript object whose eval method can be
called any number of times.
|
|
|
|
|
| |
Code which has been deprecated since 2.10.0 and which allowed
for straightforward removal.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Simplified the code paths to just use one of two `Wrapper` types
for textual templating.
Simplified the class-based template to use the same `$iw` name
for the both the class and the wrapper value. In addition,
the $read value is an object extending $read, instead of containing
an extra instance field, which keeps paths to values the same
for both templates.
Both styles trigger loading the value object by referencing the
value that immediately wraps the user code, although for the
class style, inner vals are eager and it would suffice to load
the enclosing `$read` object.
The proposed template included extra vals for values imported
from history, but this is not necessary since such an import
is always a stable path. (Or, counter-example to test is welcome.)
The test for t5148 is updated as a side effect. Probably internal
APIs don't make good test subjects.
Modify -Y option message.
|
|
|
|
|
|
|
|
| |
-Yrepl-class-based
Refactoring to reduce the number of if-else
Fix test.
|
|
|
|
|
|
|
| |
Most of this was revealed via -Xlint with a flag which assumes
closed world. I can't see how to check the assumes-closed-world
code in without it being an ordeal. I'll leave it in a branch in
case anyone wants to finish the long slog to the merge.
|
|
|
|
|
|
| |
Calling position factories rather than instantiating these
particular classes. Not calling deprecated methods. Added a few
position combinator methods.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
One last flurry with the broom before I leave you slobs to code
in your own filth. Eliminated all the trailing whitespace I
could manage, with special prejudice reserved for the test cases
which depended on the preservation of trailing whitespace.
Was reminded I cannot figure out how to eliminate the trailing
space on the "scala> " prompt in repl transcripts. At least
reduced the number of such empty prompts by trimming transcript
code on the way in.
Routed ConsoleReporter's "printMessage" through a trailing
whitespace stripping method which might help futureproof
against the future of whitespace diseases. Deleted the up-to-40
lines of trailing whitespace found in various library files.
It seems like only yesterday we performed whitespace surgery
on the whole repo. Clearly it doesn't stick very well. I suggest
it would work better to enforce a few requirements on the way in.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The handy stack trace truncation in REPL doesn't
show cause like a regular trace.
This commit fixes that and also adds the usual
indicator for truncation, viz, "... 33 more".
The example from the ticket produces:
```
scala> rewrapperer
java.lang.RuntimeException: java.lang.RuntimeException: java.lang.RuntimeException: Point of failure
at .rewrapper(<console>:9)
at .rewrapperer(<console>:10)
... 32 elided
Caused by: java.lang.RuntimeException: java.lang.RuntimeException: Point of failure
at .wrapper(<console>:8)
... 34 more
Caused by: java.lang.RuntimeException: Point of failure
at .sample(<console>:7)
... 35 more
```
Suppressed exceptions on Java 7 are handled reflectively.
```
java.lang.RuntimeException: My problem
at scala.tools.nsc.util.StackTraceTest.repressed(StackTraceTest.scala:56)
... 27 elided
Suppressed: java.lang.RuntimeException: Point of failure
at scala.tools.nsc.util.StackTraceTest.sample(StackTraceTest.scala:29)
at scala.tools.nsc.util.StackTraceTest.repressed(StackTraceTest.scala:54)
... 27 more
```
|
|\
| |
| | |
SI-6507 completely sidestep handlers in REPL when :silent in on
|
| |
| |
| |
| |
| |
| |
| |
| |
| | |
This is a cleanup of 6db8a52, the original fix for SI-6507.
When the REPL is :silent, all handlers are ignored when it comes to
generating the printed result. The result extraction code (`lazy val
resN = ...`) is still generated, but now it isn't called until the
user calls it.
|
|\ \
| | |
| | | |
SI-7740 Trim stack trace before printing in REPL
|
| | | |
|
|/ /
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
Historically calling NoSymbol.owner has crashed the compiler.
With this commit, NoSymbol owns itself. This is consistent with
the way ownership chains are handled elsewhere in the compiler
(e.g. NoContext.owner is NoContext, NoSymbol.enclClass is
NoSymbol, and so on) and frees every call site which handles
symbols from having to perform precondition tests against
NoSymbol.
Since calling NoSymbol.owner sometimes (not always) indicates
a bug which we'd like to catch sooner than later, I have
introduced a couple more methods for selected call sites.
def owner: Symbol // NoSymbol.owner is self, log if -Xdev
def safeOwner: Symbol // NoSymbol.owner is self, ignore
def assertOwner: Symbol // NoSymbol.owner is fatal
The idea is that everyone can call sym.owner without undue anxiety
or paranoid null-like tests. When compiling under -Xdev calls to
`owner` are logged with a stack trace, so any call sites for which
that is an expected occurrence should call safeOwner instead to
communicate the intention and stay out of the log. Conversely, any
call site where crashing on the owner call was a desirable behavior
can opt into calling assertOwner.
This commit also includes all the safeOwner calls necessary to
give us a silent log when compiling scala.
|
|\|
| |
| | |
SI-6507 do not call .toString on REPL results when :silent is on.
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
Member handlers used to always call .toString on REPL results, even when
:silent was on, which could force evaluation or cause unwanted side
effects.
This forwards the current value of `printResults` to the member
handlers (through Request) for them to decide what to do when the
results must not be printed.
2 handlers now do not return any extraction code when silent:
- ValHandler, so that it doesn't call toString on the val
- Assign, so that it doesn't call toString on the right-hand side
of the assignement.
|
|\ \
| | |
| | | |
SI-7681 Remove DaemonThreadFactory, clean up IMain
|
| | | |
|
| |/ |
|
|/
|
|
|
|
| |
This commit refactors repl to use `parseStats` entry point and
streamlines hacky error handling that was previously used to
encode errors that happen during parsing.
|
|\
| |
| | |
ScriptEngine.eval() forwards Error instead of new ScriptException
|
| | |
|
|/
|
|
|
| |
This can have a dramatic effect on computing time in cases with big
intermediate results but simple final one.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Confusing, now-it-happens now-it-doesn't mysteries lurk
in the darkness. When scala packages are declared like this:
package scala.collection.mutable
Then paths relative to scala can easily be broken via the unlucky
presence of an empty (or nonempty) directory. Example:
// a.scala
package scala.foo
class Bar { new util.Random }
% scalac ./a.scala
% mkdir util
% scalac ./a.scala
./a.scala:4: error: type Random is not a member of package util
new util.Random
^
one error found
There are two ways to play defense against this:
- don't use relative paths; okay sometimes, less so others
- don't "opt out" of the scala package
This commit mostly pursues the latter, with occasional doses
of the former.
I created a scratch directory containing these empty directories:
actors annotation ant api asm beans cmd collection compat
concurrent control convert docutil dtd duration event factory
forkjoin generic hashing immutable impl include internal io
logging macros man1 matching math meta model mutable nsc parallel
parsing partest persistent process pull ref reflect reify remote
runtime scalap scheduler script swing sys text threadpool tools
transform unchecked util xml
I stopped when I could compile the main src directories
even with all those empties on my classpath.
|
|
|
|
|
|
|
|
|
| |
No, this isn't busywork, how dare you suggest
such a thing. I intend my tombstone to say
HERE LIES EXTEMPORE,
WHO ELIMINATED A LOT OF SIP-18 WARNINGS
REST IN PEACE
|
|
|
|
|
|
| |
Some unused private code, unused imports, and points where
an extra pair of parentheses is necessary for scalac to have
confidence in our intentions.
|
|
|
|
|
|
|
| |
This commit shortens expressions of the form `if (settings.debug.value)` to
`if (settings.debug)` for various settings. Rarely, the setting is supplied
as a method argument. The conversion is not employed in simple definitions
where the Boolean type would have to be specified.
|
|
|
|
|
|
|
|
|
|
|
|
| |
Better binding mecanism : formerly done through the default SimpleBindings
shipped with the API, it now goes through a custom IBindings class
which uses the bind method of the interpreter instead of simply
making the bindings available as a Map.
Reflexive access : the script engine is made available to itself
through a bound variable "engine" of type javax.script.ScriptEngine.
This will allow "variable injection" i.e. programmatic redefinition
of variables, among others.
|