The other day one colleague was talking about a JavaScript heavy application on which he was working, and he mentioned that for private members they were using just conventions (the typical "_" or "__" stuff). I much agree with that approach, it's the one I tend to follow and I think it's enough. Anyway, it made me think of those many articles and techniques for simulating private members in JavaScript. All of them boil down to using closures in one way or another, and all of them seem to have some sort of caveat, either you have to add your methods at the instance level instead of at the prototype level or you end up with no way (I think) to declare private instance data fields, that's what happens with my favorite solution, that is this one I found here:
function Restaurant() { } Restaurant.prototype = (function() { var private_stuff = function() { // Private code here }; return { constructor:Restaurant, use_restroom:function() { private_stuff(); } }; })();
The funny thing here is that with this approach we obtain private members rather "more private" than in other languages featuring private members out of the box. Both in C# and Java you can use reflection to invoke, get or set private members. For example, to invoke a private method in C# you would just do something like this:
//first get the method info MethodInfo method = typeof(Person).GetMethod("BeingCalledBy", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); //know you can invoke this way (in this case if you check the StackTrace from the invoked method, you'll see it invoked by _InvokeMethodFast method.Invoke(p, null); //or create a matching delegate and invoke it through it (in this case if you check the StackTrace from the invoked method, you'll see it as invoked by the calling method) Action action = (Action)(Delegate.CreateDelegate(typeof(Action), p, method)); action();
With JavaScript, I can't think of anyway to access the members of a closure from outside, unless that the closure itself is giving you that access as I explained here
I remember having read years ago a rather stupid story claiming that this was a security vulnerability. Obviously it's not, as Private members are not a security feature, they're just a way to discourage clients of a class from using them because they could change in the future breaking your code. It's the same for example as you should use only Win32 functions, and not the Windows Native API
Pondering about all this, I stumpled upon this question where someone asks whether it's possible to prevent the invocation of a private method via Reflection. Well, thought I don't think one should care about this (as I've said I don't consider it a security feature, so if someone wants to risk to use code that was not intended to be used from outside the class, at your own risk...) I considered an interesting thought exercise. I've figured a rather basic way to do this, fighting Reflection with Reflection!
Simply put, you can know in one method who's invoking it by using new StackTrace().GetFrame, you can also know the list of methods in your class (that are the only places from which that private method should be legally invoked...) so with these 2 things, it's just a matter to do the match
public class MethodBaseComparer : IEqualityComparer{ private string GetMethodIdentifier(MethodBase mb) { return mb.Name + ":" + String.Join(";", mb.GetParameters().Select(paramInfo=>paramInfo.Name).ToArray()); } public bool Equals(MethodBase m1, MethodBase m2) { //we need something more here, comparing just by name is not enough, need to take parameters into account return this.GetMethodIdentifier(m1) == this.GetMethodIdentifier(m2); } public int GetHashCode(MethodBase mb) { return this.GetMethodIdentifier(mb).GetHashCode(); } } class PrivacyHelper { static Dictionary cache = new Dictionary (); public static bool IsInvocationAllowed () { Type curType = typeof(T); if (!cache.ContainsKey(curType)) { cache[curType] = curType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).ToArray(); } //StackTrace.GetFrame returns a MethodBase, not a MethodInfo, that's why we're falling back to MethodBody MethodBase invoker = new StackTrace().GetFrame(2).GetMethod(); return cache[curType].Contains(invoker, new MethodBaseComparer()); } } //now, in whatever method that you want to protect you would add something like this: private string SayInternalSecure() { if (!PrivacyHelper.IsInvocationAllowed ()) throw new Exception("you can't invoke this private method"); //run normal code here } }
One of the many problems that I can think of for this (sort of) solution, is that if for some odd reason I wanted to invoke via Reflection (MethodInfo.Invoke) a private method from any other of my other methods, I would also get it prevented, cause invoking through MethodInfo.Invoke, the caller I would obtain would be _InvokeMethodFast, instead of the method that is using the Reflection hack.
You can get the code here
No comments:
Post a Comment