Prevent optimizing specific strings? - google-closure-compiler

Consider this code:
var tmpString = '<% user.firstName %>';
if (tmpString === 'John') {
console.log('There user is john');
}
Closure compiler turns that code into this:
Yes that's not a mistake, the field above is empty because technically that code is dead and has no side effects, so closure compiler does its job.
But here's the catch, the "string" is actually using a template literal, eventually a name is going to show up there, and it can be John
Is there a way, maybe with some sort of annotation, to tell closure compiler that tmpString has a side effect and that is outside it's scope, so then maybe closure compiler won't optimize it?

Related

First-Class Citizen

The definition of first-class citizen found in the wiki article says:
An object is first-class when it
can be stored in variables and data structures
can be passed as a parameter to a subroutine
can be returned as the result of a subroutine
can be constructed at run-time
has intrinsic identity (independent of any given name)
Can someone please explain/elaborate on the 5th requirement (in bold)? I feel that the article should have provided more details as in what sense "intrinsic identity" is capturing.
Perhaps we could use functions in Javascript and functions in C in our discussion to illustrate the 5th bullet.
I believe functions in C are second-class, whereas functions are first-class in Javascript because we can do something like the following in Javascript:
var foo = function () { console.log("Hello world"); };
, which is not permitted in C.
Again, my question is really on the 5th bullet (requirement).
Intrinsic identity is pretty simple, conceptually. If a thing has it, its identity does not depend on something external to that thing. It can be aliased, referenced, renamed, what-have-you, but it still maintains whatever that "identity" is. People (most of them, anyway) have intrinsic identity. You are you, no matter what your name is, or where you live, or what physical transformations you may have suffered in life.
An electron, on the other hand, has no intrinsic identity. Perhaps introducing quantum mechanics here just confuses the issue, but I think it's a really fantastic example. There's no way to "tag" or "label" an electron such that we could tell the difference between it and a neighbor. If you replace one electron with another, there is absolutely no way to distinguish the old one from the new one.
Back to computers: an example of "intrinsic identity" might be the value returned by Object#hashCode() in Java, or whatever mechanism a JavaScript engine uses that permits this statement to be false:
{} === {} // false
but this to be true:
function foo () {}
var bar = foo;
var baz = bar;
baz === foo; // true

element() vs. node() in XQuery

Can someone tell me the exact difference between node() and element() types in XQuery? The documentation states that element() is an element node, while node() is any node, so if I understand it correctly element() is a subset of node().
The thing is I have an XQuery function like this:
declare function local:myFunction($arg1 as element()) as element() {
let $value := data($arg1/subelement)
etc...
};
Now I want to call the function with a parameter which is obtained by another function, say functionX (which I have no control over):
let $parameter := someNamespace:functionX()
return local:myFunction($parameter)
The problem is, functionX returns an node() so it will not let me pass the $parameter directly. I tried changing the type of my function to take a node() instead of an element(), but then I can’t seem to read any data from it. $value is just empty.
Is there some way of either converting the node to an element or should am I just missing something?
EDIT: As far as I can tell the problem is in the part where I try to get the subelement using $arg1/subelement. Apparently you can do this if $arg1 is an element() but not if it is a node().
UPDATE: I have tested the example provided by Dimitre below, and it indeed works fine, both with Saxon and with eXist DB (which is what I am using as the XQuery engine). The problem actually occurs with the request:get-data() function from eXist DB. This function gets data provided by the POST request when using eXist through REST, parses it as XML and returns it as a node(). But for some reason when I pass the data to another function XQuery doesn’t acknowledge it as being a valid element(), even though it is. If I extract it manually (i.e. copy the output and paste it to my source code), assign it to a variable and pass it to my function all goes well. But if I pass it directly it gives me a runtime error (and indeed fails the instance of test).
I need to be able to either make it ignore this type-check or “typecast” the data to an element().
data() returning empty for an element just because the argument type is node() sounds like a bug to me. What XQuery processor are you using?
It sounds like you need to placate static type checking, which you can do using a treat as expression. I don't believe a dynamic test using instance of will suffice.
Try this:
let $parameter := someNamespace:functionX() treat as element()
return local:myFunction($parameter)
Quoting from the 4th edition of Michael Kay's magnum opus, "The treat as operator is essentially telling the system that you know what the runtime type is going to be, and you want any checking to be deferred until runtime, because you're confident that your code is correct." (p. 679)
UPDATE: I think the above is actually wrong, since treat as is just an assertion. It doesn't change the type annotation node(), which means it's also a wrong assertion and doesn't help you. Hmmm... What I really want is cast as, but that only works for atomic types. I guess I'm stumped. Maybe you should change XQuery engines. :-) I'll report back if I think of something else. Also, I'm curious to find out if Dimitre's solution works for you.
UPDATE #2: I had backpedaled here earlier. Can I backpedal again? ;-) Now my theory is that treat as will work based on the fact that node() is interpreted as a union of the various specific node type annotations, and not as a run-time type annotation itself (see the "Note" in the "Item types" section of the XQuery formal semantics.) At run time, the type annotation will be element(). Use treat as to guarantee to the type checker that this will be true. Now I wait on bated breath: does it work for you?
EXPLANATORY ADDENDUM: Assuming this works, here's why. node() is a union type. Actual items at run time are never annotated with node(). "An item type is either an atomic type, an element type, an attribute type, a document node type, a text node type, a comment node type, or a processing instruction type."1 Notice that node() is not in that list. Thus, your XQuery engine isn't complaining that an item has type node(); rather it's complaining that it doesn't know what the type is going to be (node() means it could end up being attribute(), element(), text(), comment(), processing-instruction(), or document-node()). Why does it have to know? Because you're telling it elsewhere that it's an element (in your function's signature). It's not enough to narrow it down to one of the above six possibilities. Static type checking means that you have to guarantee—at compile time—that the types will match up (element with element, in this case). treat as is used to narrow down the static type from a general type (node()) to a more specific type (element()). It doesn't change the dynamic type. cast as, on the other hand, is used to convert an item from one type to another, changing both the static and dynamic types (e.g., xs:string to xs:boolean). It makes sense that cast as can only be used with atomic values (and not nodes), because what would it mean to convert an attribute to an element (etc.)? And there's no such thing as converting a node() item to an element() item, because there's no such thing as a node() item. node() only exists as a static union type. Moral of the story? Avoid XQuery processors that use static type checking. (Sorry for the snarky conclusion; I feel I've earned the right. :-) )
NEW ANSWER BASED ON UPDATED INFORMATION: It sounds like static type checking is a red herring (a big fat one). I believe you are in fact not dealing with an element but a document node, which is the invisible root node that contains the top-level element (document element) in the XPath data model representation of a well-formed XML document.
The tree is thus modeled like this:
[document-node]
|
<docElement>
|
<subelement>
and not like this:
<docElement>
|
<subelement>
I had assumed you were passing the <docElement> node. But if I'm right, you were actually passing the document node (its parent). Since the document node is invisible, its serialization (what you copied and pasted) is indistinguishable from an element node, and the distinction was lost when you pasted what is now interpreted as a bare element constructor in your XQuery. (To construct a document node in XQuery, you have to wrap the element constructor with document{ ... }.)
The instance of test fails because the node is not an element but a document-node. (It's not a node() per se, because there's no such thing; see explanation above.)
Also, this would explain why data() returns empty when you tried to get the <subelement> child of the document node (after relaxing the function argument type to node()). The first tree representation above shows that <subelement> is not a child of the document node; thus it returns the empty sequence.
Now for the solution. Before passing the (document node) parameter, get its element child (the document element), by appending /* (or /element() which is equivalent) like this:
let $parameter := someNamespace:functionX()/*
return local:myFunction($parameter)
Alternatively, let your function take a document node and update the argument you pass to data():
declare function local:myFunction($arg1 as document-node()) as element() {
let $value := data($arg1/*/subelement)
etc...
};
Finally, it looks like the description of eXist's request:get-data() function is perfectly consistent with this explanation. It says: "If its not a binary document, we attempt to parse it as XML and return a document-node()." (emphasis added)
Thanks for the adventure. This turned out to be a common XPath gotcha (awareness of document nodes), but I learned a few things from our detour into static type checking.
This works perfectly using Saxon 9.3:
declare namespace my = "my:my";
declare namespace their = "their:their";
declare function my:fun($arg1 as element()) as element()
{
$arg1/a
};
declare function their:fun2($arg1 as node()) as node()
{
$arg1
};
my:fun(their:fun2(/*) )
when the code above is applied on the following XML document:
<t>
<a/>
</t>
the correct result is produced with no error messages:
<a/>
Update:
The following should work even with the most punctuential static type-checking XQuery implementation:
declare namespace my = "my:my";
declare namespace their = "their:their";
declare function my:fun($arg1 as element()) as element()
{
$arg1/a
};
declare function their:fun2($arg1 as node()) as node()
{
$arg1
};
let $vRes := their:fun2(/*)
(: this prevents our code from runtime crash :)
return if($vRes instance of element())
then
(: and this assures the static type-checker
that the type is element() :)
my:fun(their:fun2(/*) treat as element())
else()
node() is an element, attribute, processing instruction, text node, etc.
But data() converts the result to a string, which isn't any of those; it's a primitive type.
You might want to try item(), which should match either.
See 2.5.4.2 Matching an ItemType and an Item in the W3C XQuery spec.
Although it's not shown in your example code, I assume you are actually returning a value (like the $value you are working with) from the local:myFunction.

Groovy DSL with embedded groovy scripts

I am writing a DSL for expressing flow (original I know) in groovy. I would like to provide the user the ability to write functions that are stored and evaluated at certain points in the flow. Something like:
states {
"checkedState" {
onEnter {state->
//do some groovy things with state object
}
}
}
Now, I am pretty sure I could surround the closure in quotes and store that. But I would like to keep syntax highlighting and content assist if possible when editing these DSLs. I realize that the closure COULD reference artifacts from the surrounding flow definition which would no longer be valid when executing the closure in a different context, and I am fine with this. In reality I would like to use the closure syntax for a non-closure function definition.
tl;dr; I need to get the closure's code while evaluating the DSL so that it can be stored in the database and executed by a script host later.
I don't think there is a way to get a closure's source code, as this information is discarded during compilation. Perhaps you could try writing an AST transformation that would make closure's syntax tree available at runtime.
If all you care about is storing the closure in the database, and you don't need later access to the source code, you can try serializing it and storing the serialized form.
Closure implements Serializable, and after nulling its owner, thisObject and delegate attributes I was able to serialize it, but I'm getting ClassNotFoundException on deserialization.
def myClosure = {a, b -> a + b}
Closure.metaClass.setAttribute(myClosure, "owner", null)
Closure.metaClass.setAttribute(myClosure, "thisObject", null)
myClosure.delegate = null
def byteOS = new ByteArrayOutputStream()
new ObjectOutputStream(byteOS).writeObject(myClosure)
def serializedClosure = byteOS.toByteArray()
def input = new ObjectInputStream(new ByteArrayInputStream(serializedClosure))
def deserializedClosure = input.readObject() // throws CNFE
After some searching, I found Groovy Remote Control, a library created specifically to enable serializing closures and executing them later, possibly on a remote machine. Give it a try, maybe that's what you need.

How to add a property to a map dynamically in velocity?

I have the following code
$pageName = "test";
$Container = {};
I like to set a property of $Container by a variable. I tried $Container.set("test", $pageName);. It didn't raise any errors, but $Container.test or $Container.get("test"); display nothing.
How do I fix it?
The problem is that set is the wrong method. You need to do a put. Remember - Velocity is calling the Java methods. There is no "set" method on a Map object.
Specifically, you can do
$Container.put("test", $pageName)
Now, one weird thing is that this will print "true" or "false" in the page, since the Map.put() method returns a boolean. So I always do
#set($dummy = $Container.put("test", $pageName))
which does the put and stores the result in another reference (which you can then ignore) instead of rendering it to the page.
Hey I ran into the same problem is the "true" or "false" printed on the page, and there is a simpler way to handle it. What I did is a little weird, and I did it Confluence, which of course uses Velocity under the covers. I mention that because I understand Velocity can be used in may different applications.
With a Confluence user macro, I check for a previously created attribute on the req variable, the request variable, i.e. "myPageVars". Then I use the put method to put a new key-value pair, based on the macro parameters. By using the $! prefix, rather than just $, the output isn't sent to the screen.
...
$!req.getAttribute("myPageVars").put( $paramKey, $paramValue )
...
I'm somewhat new to Velocity, so I can't guarantee this will work in every context, but it seems syntactically easier than the whole #set ($dummy etc. line.

Actionscript - is it better to cast or create a new variable? Or does it matter at all?

I find that in my daily Flex/Flash work, I do this number a lot:
//Calling a function...
MyCustomObject(container.getChildAt(i)).mySpecialFunction();
The question is - is this the best way to do this? Should I do this:
//Calling a function
var tempItem:MyCustomObject = container.getChildAt(i) as MyCustomObject;
tempItem.mySpecialFunction();
It might be incidental but I'm just wondering if there is an "accepted" way or a preferred way to do this. The second option seems more readable but I wonder if it takes more of a performance hit to create a new variable. Or does it all come down to style and preference?
It's important to remember that there is a difference between explicit casting and using the as keyword. Casting throws an error when it fails, whereas the as keyword does not (it just returns null).
// a casting error
try {
var number:int = 666;
var urlreq:URLRequest = URLRequest( number );
} catch(e:TypeError) {
// TypeError: Error #1034: Type Coercion failed: cannot
// convert 666 to flash.net.URLRequest.
trace(e);
}
Whereas the as keyword fails silently:
var number:int = 666;
var urlreq:URLRequest = number as URLRequest;
trace(urlreq); // prints null to the debug pane
Personally, I bear these behaviours in mind when deciding method to use. Generally, I'd recommend casting explicitly, as you'll know exactly how/when a cast failed. Often, though, you might want to fail silently and continue.
It generally doesn't matter. Creating a var just creates a pointer to the object, so it's not using more memory or anything like that.
The second example is definitely more readable and debuggable and should thus be preferred.
The risk you run from creating temp vars is that you might delay or prevent garbage collection for that object. This generally isn't a problem when it's just a local var in a function; just keep scope in mind when you're creating vars and passing them around.
For in-depth on the subject, read Grant Skinner's series on resource management in AVM2:
http://www.gskinner.com/blog/archives/2006/06/as3_resource_ma.html
for the second example you might want to test for the nullity to avoid a NullPointerException when invoking "mySpecialFunction", e.g.
var tempItem:MyCustomObject = container.getChildAt(i) as MyCustomObject;
if ( tempItem )
{
tempItem.mySpecialFunction();
}
I usually prefer the second approach but you have to remember that you can only use the as operator for casting "Date" and "Array" types.

Resources