Search This Blog

To adblock users

Hello! If you see this, you are most likely using an ad blocker. (Or maybe you have JavaScript disabled. Or maybe my web server is down.) I have no problem with ad blockers; in fact I use one myself. If a site tries to deny me access unless I disable it, I just find a way to circumvent that. But if a site politely asks me to do so, but still allows access to the site, I disable it for the site. I am asking you to please do the same for this site. I can't make you, but I would appreciate it. Thank you! :-)

Friday, February 01, 2013

Disabling Mathematica's "Locked" attribute

In Wolfram's Mathematica, there's an attribute called Locked which, when set, makes it impossible for the end-user to change that symbol's attributes, including by removing that attribute. (Note that I use the word "impossible" loosely.) Now this is a problem, because there are other attributes that do things like prevent Mathematica from showing you the source code to functions. To make matters worse, it contains a function that encodes Mathematica package files in a way that Mathematica is able to execute, but won't show the user, which makes it difficult to remove the offending lines of code.

Mathematica 9 was recently released, and unfortunately this does not work anymore in Mathematica 9. (See below for an experimental method for version 9.) If you have a copy of Mathematica 8 or earlier, however, you can prevent this attribute from being set on imported symbols. You simply need to take advantage of a loophole in the way Mathematica imports package files, encoded ones included.

Normally, when you load a package file, you do something like Get["package.m"]. When you do this, it keeps user definitions of symbols, and respects them when reading in the source code. So all we need to do is redefine Locked to an empty Sequence, so it will be automatically omitted from any lists it appears in. The Locked attribute is protected, but isn't itself Locked until Mathematica 9.

Here's the code you need to run before loading the package (put it in your init.m):

Unprotect[Locked]
Locked = Sequence[]

Now the package will be imported with everything unlocked.

One potential problem with this is that a clever programmer might see this coming and remove your definition of Locked at the beginning of the package. We could Protect it, but the package could still Unprotect it. Say, isn't there an attribute that prevents this?

In case it wasn't obvious, the solution is to Lock your new definition of Locked. I love the irony here, by the way. But remember, we redefined Locked to be harmless, didn't we? So the solution to this is kind of tricky.

Mathematica includes a function called Block. What this function does is let you execute code using temporary values for variables which are then reset to what they were before. If no value is given for a variable, it temporarily clears that variable's definition. Normally this function resets the symbol's attributes as well, but it doesn't if the Locked attribute becomes set during execution, so this still works.

So the new code would be:

Unprotect[Locked]
Locked = Sequence[]
Block[{Locked}, Attributes[Locked] = {Protected, Locked}]

Now when the package loads, you'll probably see something like this, but the package will still be imported just fine.

Protected::locked : Symbol Locked is locked.
ClearAll::locked : Symbol Locked is locked.

All that's left to do now is un-ReadProtect the functions you want to see. Now that they aren't Locked, this won't be a problem. Have fun! :-)

Edit: In hindsight, it may be possible for a package to do the same thing with Block. I imagine I would have tested that, but I don't remember for sure, and unfortunately I don't have Mathematica 8 installed anymore, so I can't test now to be sure. Luckily, however, the method described below should work as well.

Update: It is also possible to actually rename the Locked attribute internally, which does work on Mathematica 9, as well as 10. This does have some side effects however; for instance, if a package tries to add that attribute using SetAttributes, none of the given attributes will be added. But at least you'll still be able to un-ReadProtect whatever you want. All you have to do is open mathdll.dll (located in [your Mathematica install directory]/SystemFiles/Kernel/Binaries/[your platform folder]) in a hex editor. Then search for the string "Locked" in the file. Make sure it's not part of something longer, like "LockedStack". Find one where it just says "Locked". Then just change it to some other six bytes that won't get in the way.

Yet another workaround: If you just want to see the definitions of functions, encasing the package's Get in Trace[] will also show some definitions, based on my testing. The ideal solution, of course, would be a method of decoding packages encoded using Encode, but I haven't been able to figure it out.