I'm using the closure compiler to minify and speed up my code but I'm running into some issues with JSLint when I try to export my functions.
Basically, I have an object, foo{} with a function, foo.bar() that gets called via an external file as. In order for this function to be called externally I need to add some declarations to my script before it gets compiled:
window['foo'] = foo;
window['foo']['bar'] = foo.bar;
This works great, but—as ever—JSLint thinks I'm mental for even attempting this. I've managed to suppress the dot notation error by declaring, /*jslint sub: true */ just before these two lines but I still get the following error:
"window['foo']['bar'] = foo.bar;" - Weird assignment
It's not wrong, it is a weird assignment out of context, but I need it in there in order for my code to work.
The way I see it, I have three possible options:
Tell JSLint not to bother even looking at them two lines.
Suppress the Weird assignment error.
Find another way to make my code work with closure compiler.
The problem is, I have no idea how to go about doing any of them.
You can export names using goog.exportSymbol instead of bracket notation: https://github.com/google/closure-library/blob/master/closure/goog/base.js#L1532
The Closure Compiler understands what goog.exportSymbol is so it'll remove the explicit exportSymbol call and add foo and bar directly to the window for you.
Related
In code that I help maintain, I have found multiple examples of code that looks like this:
Description := IfThen(Assigned(Widget), Widget.Description, 'No Widget');
I expected this to crash when Widget was nil, but when I tested it, it worked perfectly.
If I recompile it with "Code inlining control" turned off in Project - Options - Compiler, I do get an Access Violation.
It seems that, because IfThen is marked as inline, the compiler is normally not evaluating Widget.Description if Widget is nil.
Is there any reason that the code should be "fixed", as it doesn't seem to be broken? They don't want the code changed unnecessarily.
Is it likely to bite them?
I have tested it with Delphi XE2 and XE6.
Personally, I hate to rely on a behavior that isn't contractual.
The inline directive is a suggestion to the compiler.
If I understand correctly what I read, your code would also crash if you build using runtime packages.
inlining never occurs across package boundaries
Like Uli Gerhardt commented, it could be considered a bug that it works in the first place. Since the behavior isn't contractual, it can change at any time.
If I was to make any recommendation, I would flag that as a low priority "fix". I'm pretty sure some would argue that if the code works, it doesn't need fixing, there is no bug. At that point, it becomes more of a philosophical question (If a tree falls in a forest and no one is around to hear it, does it make a sound?)
Is there any reason that the code should be "fixed", as it doesn't seem to be broken?
That's really a question that only you can answer. However, to answer it then you need to understand fully the implications of reliance on this behaviour. There are two main issues that I perceive:
Inlining of functions is not guaranteed. The compiler may choose not to inline, and in the case of runtime packages or DLLs, a function in another package cannot be inlined.
Skipping evaluation of an argument only occurs when the compiler is sure that there are no side effects associated with evaluation of the argument. For instance, if the argument involved a function call, the compiler will ensure that it is always evaluated.
To expand on point 2, consider the statement in your question:
Description := IfThen(Assigned(Widget), Widget.Description, 'No Widget');
Now, if Widget.Description is a field, or is a property with a getter that reads a field, then the compiler decides that evaluation has no side effects. This evaluation can safely be skipped.
On the other hand, if Widget.Description is a function, or property with a getter function, then the compiler determines that there may be side effects. And so it ensures that Widget.Description is evaluated exactly once.
So, armed with this knowledge, here are a couple of ways for your code to fail:
You move to runtime packages, or the compiler decides not to inline the function.
You change the Description property getter from a field getter to a function getter.
If it were me, I would not like to rely on this behaviour. But as I said right at the top, ultimately it is your decision.
Finally, the behaviour has been changed from XE7. All arguments to inline functions are evaluated exactly once. This is in keeping with other languages and means that observable behaviour is no longer affected by inlining decisions. I would regard the change in XE7 as a bug fix.
It already has been fixed - in XE7 and confirmed that this was supposed to be wrong behavior.
See https://quality.embarcadero.com/browse/RSP-11531
HHVM 3.9 is not such a fan of ternary statements with named functions, even when passed through fun(), but ≥3.10 is totally fine with them. It seems as though this is one of few cases, however, because 3.9 does accept named functions returned from concrete functions, as well as accepting named functions passed into other functions (3v4l):
<?hh
echo ((() ==> fun('strlen'))())('Hello'); // 5
echo (($f, $v) ==> $f($v))(strlen, 'Hello'); // 5 + Notice: Use of undefined constant strlen - assumed 'strlen'
echo (true ? strlen : intval)('100'); // Fatal error: syntax error, unexpected '(', expecting ',' or ';' on line 3
What changed between 3.9 and 3.10? Are there any cases in HHVM ≥3.10 where named functions cannot be referenced and used this way?
First, when writing Hack, don't write your code at toplevel; the hh_client typechecker can't check anything at toplevel. And 3v4l doesn't run the typechecker at all, you need to run it locally.
That said, no, Hack doesn't really have first-class functions. Most of its behaviour here it inherited from PHP, which also doesn't have them. Back when I was working on the Hack team, we tossed around a lot of ideas for adding them to the language; it's an obvious addition and need. But the need was never quite strong enough such that we sat down and actually worked out the details for both the type system and runtime implications; in particular, how to work out some of the scoping issues that the current callables have. Anonymous functions fill out enough of the need, especially with Hack's short lambda syntax, that there was always something more pressing to deal with.
So Hack just has PHP's normal callable forms; fun is one of a few special functions which give information to the typechecker that the string you specified actually represents a function so the typechecker can do proper type analysis. But at the end of the day, fun just boils down to the usual PHP callable forms with a bit of extra magic in the typechecker.
As to the behaviour you indicate in your 3v4l link. Using strlen and intval like that would cause a type error in Hack, since those are syntactically constants but constants with those names don't exist since Hack doesn't have first-class functions -- or it would if the code weren't at toplevel and you were running the typechecker. As to why it causes a parse error in HHVM 3.9 (which masks the "invalid constant" errors you see in 3.10), I'm not 100% sure. Judging from this example which works in PHP7 and HHVM 3.10, but not PHP5 and HHVM 3.9, my guess it is a PHP7 feature that is backwards compatible and so is always enabled in HHVM.
My delphi application runs scripts using JvInterpreter (from the Jedi project).
A feature I use is runtime evaluation of expressions.
Script Example:
[...]
ShowMessage(X_SomeName);
[...]
JvInterpreter doesn't know X_SomeName.
When X_SomeName's value is required the scripter calls its OnGetValue-callback.
This points to a function I handle. There I lookup X_SomeName's value and return it.
Then JvInterpreter calls ShowMessage with the value I provided.
Now I consider switching to DelphiWebScript since it has a proper debug-interface and should also be faster than JvInterpreter.
Problem: I didn't find any obvious way to implement what JvInterpreter does with its OnGetValue/OnSetValue functions, though.
X_SomeName should be considered (and actually is, most of the time) a variable which is handled by the host application.
Any Ideas?
Thanks!
You can do that through the language extension mechanism, which has a FindUnknownName method that allows to register symbols on the spot.
It is used in the asm lib module demo, and you can also check the new "AutoExternalValues" test case in ULanguageExtensionTests, which should be closer to what you're after.
I have an application that periodically will run a Lua script. Within the script, on occasion, I have created a custom registered Lua function to check some parameters and decide if the Lua script should continue or exit. The logic ideally should not be part of the script and I can think of using a Lua script to work around this, but I'm wondering if it is possible to stop the execution of a Lua script without ending the application.
I have a custom function written in Delphi and exposed to Lua scripts using Lua 5.1. The Lua script looks something like that shown below and the script in Lua is started using luaL_loadbuffer.
io.write("Script starting\n");
--Custom Function
ExitIfFound();
io.write("Script continuing\n");
My custom function looks something like this, below I have provided one of my attempts where I tried to use lua_error to stop the script...
function ExitIfFound(LuaState: TLuaState): Integer;
var
s: AnsiString;
begin
s := 'ExitIfFound ending script, next Lua script line not called';
lua_pushstring(LuaState, PAnsiString(s));
lua_error(LuaState);
end;
When my custom function is called, I'm unsure as to how to exit the Lua script without any further evaluation. I have seen posts referring to Lua and using setjmp and longjmp in C, but I'm curious how these may translate Delphi.
In the example above, when I use lua_error, the entire program crashes with Windows doing its typical, [luarun.exe has stopped working] ...
With all of this, I'm am still pretty new to integrating Lua to Delphi and hoping that I can find some cleaner options to explore.
There is no clean way to entirely abort a Lua script. The lua_error function is the correct way to signal an error. It is the caller's responsibility to catch the error and propagate it to the next caller.
If you cannot rely on the caller to cooperate, then you can try to exert more control by installing debug hooks. Then the host program will be consulted before continuing to run the script. However, the script can still avoid exiting by using pcall to catch any errors.
The crash in your program is probably not simply from setting an error. Rather, it's likely from using the wrong calling convention on your ExitIfFound function. It needs to be cdecl, but Delphi's default, if you don't specify anything else, is register. Using the wrong calling convention will give you unpredictable parameter values and can lead to a corrupted stack. If you type-casted the function or used the # operator when you called lua_register, then you might have hidden the calling-convention mismatch from the compiler's type checker, which would have otherwise alerted you to the problem at compile time.
When compiled as C++, lua_error will use a exception instead of longjmp, but either way, the caller always catches the error. Exceptions are important, though, when your Delphi code uses compiler-managed types like string, or exception-sensitive constructs like try-finally blocks. In C mode, lua_error calls longjmp to jump directly to the waypoint set by a previous call to setjmp. That jump will skip over any exception handlers like the ones the Delphi compiler sets up to ensure the finally block runs and the string gets cleaned up.
A further headache is that since the compiler cleans up the string while exiting the function, the pointer you put on the Lua stack might not be valid by the time it's used; that depends on whether lua_pushstring makes a copy of its argument.
When debugging in Delphi, an exception will correctly tell me the line of code causing the fault, but I cannot get access to any local variables. Is this a limitation in the debugger? Or am I missing something simple? At present, I have to mirror all local variables to a global on the line before the fault, recompile the program and hope to be able to repeat the same exception.
For example
MyArray[I]:=Foo(...);
If I is out of bounds (with bounds checking turned on), I cannot see what the variable I is, unless I mirrored it to a globally scoped debug variable on the previous line.
Or if I have
MyInteger:=Trunc(MyFloat),
and MyFloat is 6.1E+17, I have no idea what it's value is.
You can see the values of local variables when you select the proper line in the call stack window. It is usually one or two lines before the exception is raised.
I don't have the exact version at hand when this has been implemented, but it is definitely one of the newer versions.
The "problem" is caused by the compiler as far as I know. The optimization feature of the compiler acts like a garbage collector, it frees the variables declared within a function when not used any more.
To overcome the problem, write a exception handler and make a fake use of the variable within the exception catch block.