A first class function is one where the function is available on its own. C, C++, and Ruby allow this approach. Java requires the function to be tied to a class and only provides a metadata representation of it, even if that class is merely a static collection of functions. C# supports first class functions with lambdas (which are based off of lambda calculus) and delegates.
Ruby is one of the languages that truly supports first class functions. The difference is that not only can you define functions on their own, but you can pass them as arguments and invoke methods on them. Check out Ruby's Proc object which is used to represent an arbitrary block of code.
The end result of having first class functions is the fact that it lends to some very powerful and flexible coding constructs. This is distinctly different than hacking around first class functions using the reflection API.
Java doesn't have full support of first class functions. The reflection API can give you some semblance of first class functions if the Method
object is referencing a static
method. In essence you can invoke a static method like this:
Method reference = mathClass.getMethod("sqrt");
// NOTE: the first parameter is for the object instance,
// but for static methods it is ignored
double answer = (double)reference.invoke(null, 4);
As soon as you are working with an instance method, you lose that first class function ability. You might be able to hack together some reflection based delegate support similar to C#, but the resulting code will be much slower. The "delegate" would take care of keeping the object reference for future invocations.
There is really very little you can do (in all browsers) to protect global variables.
However a common (good) practice is to encapsulate all of your scripts in (IIFE) functional closures.
(function() {
var parent = {
name: "parent",
print: function(){
console.log("Hello, "+this.name);
}
};
var child = {
name: "child",
print: function(){
console.log("Hi, "+this.name);
}
};
parent.print()
// This will print: Hello, parent
child.print()
// This will print: Hi, child
})();
This means that you may only use parent
and child
inside the scope of that function. While that is limiting, it is also protecting. If you needed them globally, one option would be maintain a namespace object:
var myGlobals = {};
(function( global ) {
global.parent = {
name: "parent",
print: function(){
console.log("Hello, "+this.name);
}
};
global.child = {
name: "child",
print: function(){
console.log("Hi, "+this.name);
}
};
global.parent.print()
// This will print: Hello, parent
global.child.print()
// This will print: Hi, child
})(myGlobals);
Or if you really needed them as individual globals, you could just do:
(function() {
window.parent = {
name: "parent",
print: function(){
console.log("Hello, "+this.name);
}
};
window.child = {
name: "child",
print: function(){
console.log("Hi, "+this.name);
}
};
parent.print()
// This will print: Hello, parent
child.print()
// This will print: Hi, child
})();
But that leaves you back at square one. In this case you would need to make sure that you do something to protect those variables at the time of shuffling. Instead of writing:
temp =parent;
parent = child;
child = temp;
You may write:
(function( parent, child ) {
// Do some stuff.
// Note: parent and child are references,
// although they are switched, something like:
// child.hello = "hello";
// will still edit the `parent` object in the outer scope
})(child, parent); // Pass them in swapped.
Freeze and Seal (two new JS Object
methods) are worth noting in this thread despite not exactly solving your problem nor having cross-browser support yet.
Best Answer
I would suggest using obfuscation. It doesn't prevent reflection, but it'll make it practically impossible (technically, someone could still do it but it'll take them far more effort to figure it all out) for someone to figure out what part of the code does what, as it'll (amongst other things) scramble the names of functions.
You can usually set exclusions, such as functions that need to be publically accessible for an API.
Unfortunately, it'll only work for your own code, and not Minecraft itself, but it's the best practical solution I can think of.
Otherwise, I think there might be a solution that involves making your own custom loader for Minecraft with a SecurityManager that sandboxes everything and prevents reflection, but I'm not entirely sure if that's possible. You may want to look at this question on StackOverflow which discusses that possibility further.