We had a big chaos with type conversion. That caused a bug where passing a key to a function that required a string, or vice versa, crashed the script.
Diminish the chaos by modifying the parameters just prior to invocation (in lslfoldconst). We also remove the, now unnecessary, calls to force floats, either alone or within vectors or quaternions.
The previous commit didn't work as expected. "from module import var" freezes the value at load time; changing it later has no effect. A reference to the module needs to be used.
Fix that and the similar problem with LSO. Also revert some "from lslcommon import *" introduced earlier.
That also revealed another bug about missing 'cond' in the import list of lslextrafuncs. This should fix all functions that return values on null key input.
Instead of using an option in the command line, use a global in lslcommon, settable by the main program (only the main LSLCalc program, which differs from LSL-PyOptimizer's main, changes it).
Failure to do so caused a regression test to fail. Harmless, because that option is overriden by main, but fixed.
Bug was introduced in commit 397dc89, with the requirement that the 'optimize' option be active for output optimizations to be applied, by forgetting to update the function header to add that default option.
lslcalc is currently derived from a snapshot of PyOptimizer at some point in past, making it difficult to maintain when bug fixes are applied to the optimizer. Solve this by incorporating expression evaluation capabilities.
llBase64ToString doesn't behave like llUnescapeURL wrt invalid UTF-8. First, the last NUL if any is removed, and the remaining NULs are converted to "?". Second, all overlong sequences are converted to a single "?", *including the 5- and 6-byte UTF-8*.
Implement this behavour and the corresponding unit tests.
LSO strings are byte arrays, but our strings are made for Mono which uses Unicode, and invalid UTF-8 sequences can't be stored in Unicode without using a custom representation.
One possible representation is to only use the codepoints 0-255 in the Unicode string, to avoid supporting multiple types for strings. Something to study in future.
Our UTF-8 validity checker failed to recognize that characters in the surrogate range (D800-DFFF) were invalid. Fortunately, Python 2 is happy about that, therefore it doesn't crash (Python 3 fixed that range too). Unfortunately, SL isn't, therefore we fix it.
Added corresponding unit tests.
This option normally takes effect through the base class in lsloptimize.py, which doesn't call the optimization techniques if deactivated. However, lsloutput.py is not called by it, and it applied some optimizations on its own.
Fixed on the reading of the optimization options, by filtering them by whether the optimize option is active.
Literal strings were not conforming to Mono's strict "NUL is end-of-string" rules, so a file with an embedded NUL within a string literal would let the NUL be part of the string. Mostly academic, because it's probably not possible to upload a file with an embedded NUL to SL, but fixed it regardless.
As an enhancement over LSL, we trigger a Type Mismatch when there are void expressions in list constructors, because in LSL, while accepted, they trigger an ugly runtime exception.
This works fine, but then expression lists, where this are checked, are not exclusive of list constructor; they are used in other places. One of these places is the initializer and the iterator of FOR loops. As a consequence, we didn't allow void functions in the FOR initializer or iterator.
Fix by adding another possible value to the parameter 'expected_types' in Parse_expression_list. False means don't allow void either (what Null did before); Null now means allow anything. All callers to Parse_expression_list are changed accordingly. Added corresponding regression test.
This caused "Label not defined within scope" when breakcont was active:
default{timer(){jump x;while(0)@x;}}
The problem was that breakcont opens a new scope for the case where it has to deal with a loop which is the single statement of an outer statement, e.g.
default{timer(){if(1)while(1)break;}}
would add braces to jump to the correct break point:
default{timer(){if(1){while(1)jump brk;@brk;}}
To avoid excessive complication, a new scope was always opened for the whole statement in each of the loops, regardless of whether we needed braces later on or not. That should be transparent most of the time, but then if the statement was a label declaration, the label would be in a new scope that would be invisible outside the loop.
Fix that by checking explicitly for a label to temporarily get rid of the new scope in that case, and add a test case for it.
The pragma warnings were duplicated, one during globals scan, another during actual parsing. This should fix it.
This is somewhat potentially dangerous, as some directives (pragmas, notably) could in future affect the global scan phase by changing the language.
Commit b73805e introduced lazy lists in assignments only. Commit 890e960 generalized it, allowing any identifier to be followed by brackets, removing the need for assignment-specific treatment. However, we forgot to remove the assignment-specific code parsing, so do it now.
'(float)"-nan"' doesn't return Indet in LSL:
llOwnerSay(llList2CSV([(float)"-nan"])); // outputs "nan", not "-nan"
Therefore, for the output to yield the correct result we have to use a different strategy to generate an indeterminate. We choose '(1e40*0)' which is shorter than the rest.
Also, we don't output infinites as '(float)"[-]inf"' but alwas as '1e40' or '(float)-1e40' (or just '-1e40' if we're in globals).
All three functions have in SL the Intel problem of inaccuracy with high values. Since we work in single precision, it's barely detectable usually, but for large input numbers the difference between the imprecise results of SL and the more accurate results of recent glibc become more obvious.
This change brings back the Intel inaccuracy, even across systems (different versions of Python/C might behave differently otherwise).
Reference:
https://randomascii.wordpress.com/2014/10/09/intel-underestimates-error-bounds-by-1-3-quintillion/
It was designed so that only ints and floats should be accepted. But for safety, and for reuse, make that "any non-float" so that long integers are also truncated to F32.
We were using a very dubious method to distinguish an indeterminate from a NaN, via struct.unpack. Turns out that math.copysign gets the job done and seems more robust.
Besides dividing by zero, any result producing NaN including inf/inf, NaN/anything, anything/NaN causes a math error as well. We only contemplated NaN/anything and neglected the rest, so we generalize it.