Locals and args marked as closed should only be written through SETLOCAL/SETARG/etc. ops. There is an exception to this for the DEFFUN and SETCONST ops, and to maintain copy/constant invariants JM has to do this weird hack and wipe out all constants and copies when it sees such an op that could alis a non-closed local/arg:
if (script->fun && script->bindings.hasBinding(cx, innerFun->atom))
Instead, locals/args which could alias a DEFFUN or SETCONST should be marked as closed, and always synced after writes and loaded from memory for reads. This same thing is already being done for vars aliased by NAME* opcodes; we should have uniform handling for these opcodes that can read or write local names indirectly, to avoid the need to duplicate this logic elsewhere in the engine (e.g. for SSA analysis).
This is also needed for NAME accesses inside 'with' blocks. This doesn't affect JM currently because scripts using 'with' aren't compiled. See bug 652422.
Instead of doing this in the emitter, it seems easier to watch for NAME aliasing during bytecode analysis. The closed vars/args attached to the script are still only for upvars, but the trackSlot/slotEscapes info which analyses and the compiler actually use take name aliasing into account.