Compilation without an associated ScriptHost happens in a call to eval,
in which case we'll fall back to the default warning settings (because
we don't have a location which we could get settings from).
Fixes#1891.
Yeah. Aul looks up function parameters before local variables when
trying to resolve an identifier. Usually this doesn't matter, but you'll
notice it if you have a local variable and a parameter with the same
name, because the variable should be initialized to nil yet you get the
value of the parameter.
This commit introduces a new Aul directive "#warning", which can be used
to enable or disable warnings for a particular piece of code.
"#warning enable" enables all warnings.
"#warning disable" disables all warnings.
"#warning enable empty_parameter_in_call" selectively enables one
specific warning while not affecting any other.
All warnings that used to be controlled by Developer.ExtraWarnings
remain disabled by default.
Aul will now emit a warning if you type something like
if (...); return true;
(note the semicolon right after the condition). It will also warn on an
empty 'else' branch. If you actually intended to have a no-op there, use
an empty block '{}'.
Systems that don't come with getopt/getopt_long in their runtime library
need to link to our private copy; link that and use the right const-ness
for its prototype.
Instead of jumping forward and back repeatedly per iteration, we're
moving the incrementor past the body, which is when it's supposed to be
executed anyway.
Letting the constant resolver throw exceptions prevented us from doing
checking on later initializers anyway, so instead we'll send them to the
error handler. As a special bonus this makes it so we don't crash when
a global variable initializer has errors. Fixes#1850, #1855.
By continuing to generate bytecode even after an error is found, we're
able to find more syntax errors and will also be able to keep the value
stack at the expected height.
When an error occurred during codegen of a function, the current
function pointer would not be reset to 0, leading to spurious warnings
about redeclaration of functions.
Uniform variables are read from the "Uniforms" proplist set on Scenario
or on individual objects. Proplist keys are uniform names. Values can
either be an int or an array of one to four ints in C4Script. In GLSL,
the uniforms then need a matching type (int/ivec2/ivec3/ivec4). There is
no error reporting; uniforms are only set if both name and type match.
The implementation walks the "Uniforms" proplists on each Draw call. We
may need to cache the uniform maps if this turns out to be too slow.
If a parse error occurs inside a declaration, the codegen should just go
on and deal with the next declaration instead of completely giving up on
the entire script.
9caaf1e introduced an external error handler for easier testing of
error conditions. The default error handler needs to count errors and
warnings if we want to display them, but didn't.
The old parser threw a standard compile error in this case; the
AST-based parser threw an ICE, which is ultimately the same thing but
made it sound like the parser was at fault. And maybe it is, and we
should allow code like "local a; func a() {}" but that seems like it
should be a conscious design decision.
C4Value already handles refcounting properly for us, so we don't need to
do it manually. It might still be worth manually refcounting them
to avoid the boxing/unboxing overhead, but it's only needed at load and
unload so it's not a priority at the moment.
By using an extern error handler in the script engine, we can mock that
handler and make sure something that should fail actually does, instead
of having to parse log messages.
Parsing a function expression inside another function failed to reset
the code generator target to the containing function, so all following
bytecode would still be appended to the nested function, leaving the
container broken.
If a local variable in a definition was set to a proplist inside the
Definition() callback, and that proplist contained cyclic references
then those references were leaked. Typically cyclic references for
script-created proplists are broken in
C4PropListScript::ClearScriptPropLists, however definition proplists
are changed to be static proplists in
C4PropList::FreezeAndMakeStaticRecursively.
To fix this, each script host maintains a list of proplists made static
by FreezeAndMakeStaticRecursively, and explicitly deletes all of these
proplists on Clear().
This leak also leads to an assertion failure inside
C4PropListScript::ClearScriptPropLists in debug mode, and can also be
observed by C4PropList::PropLists not being empty after game clear.
The definition in Objects.ocd/Helpers.ocd/UserAction.ocd constructs
cyclic proplists in its Definition() call. A simpler, more minimal way
to provoke the leak is the following (it provokes the leak but not the
assertion failure):
local bla;
func Definition(def)
{
bla = {};
bla.test = { Name="Test222" , Options = { Name="Test333" } };
bla.test.Options.Link = { Name="Test444", Blub=bla.test };
}
Also adjust editor props for the change:
1. enum needs to create a copy, not a reference for the default value of an option if it is defined inline
2. Always use proper GetName() resolution on property group names even if a static name would be available
If a timer=1-effect registered another timer=1-effect into the same object, it could cause an endless loop. This happened if the timer in the sequence object was set to 1.
Declaring a local variable inside any function works the same way as
declaring it in top-level scope, which gets a bit weird if done inside a
function inside a constant proplist, but is at least consistent.
Thanks to Flinti for reporting this.