CrxOop: Bringing Object Oriented Programming, and Proper Prototype Based Programming, To Javascript
The aim of the library documented below is to provide developers with a solution to allow development using interfaces and classes as they are known in other object oriented programming (OOP) languages such as C++, C#, Java and PHP. Further more, V1.4 introduces structures, a generalization of the concept of prototypal inheritance, known here as POBP.

Table of contents

1.0 Introduction

1.1 Preface (IMPORTANT)

CrxOop is a javascript (JS) library whose primary aim is to allow JS developers to develop using interfaces and classes as they are known in other object oriented programming (OOP) languages such as C++, C#, Java and PHP. The aim of this documentation is to describe the available features and their syntax. The documentation does not aim to teach you OOP, nor does it aim to teach you JS. Further more, I shall rely mostly on C++ terminology and syntax throughout this documentation, with some exceptions such as the word 'interface' which is borrowed from Java. This does not mean that this terminology is exclusive to those languages, but I am simply ensuring my communication is defined. The code presented here, especially that which is written in C++, does not constitute best practice and should not be used as reference in any way beyond what is intended here.

This documentation is very brief about the actual details of OOP concepts. For further details, refer to C++'s documentation first, and if the topic is not found there, Java's documentation. A web search engine can be very handy. Needless to say, familiarity with any of C++, Java, and to some extent C# and PHP, is very useful when learning CrxOop.

Version 1.4 of the library introduces Structures, a formalization and generalization of prototypal inheritance in javascript. Structures are meant primarly to be used for data structures, but they can be used by those who prefer prototypal inheritance for certain algorithms, taking advantage of well defined and enforced accessors, well defined constructor invocation, and data typing. In other words, structures are CrxOop's implementation of Prototype Based Programming, or as this author likes to call it, Prototype Object Based Programming (POBP).

1.2 Getting Started

Two versions of the library exist. One with strict javascript mode enabled, and one without. To learn more see the section on modes.

You can download the library below:

Strict JS Mode: DOWNLOAD LINK

Normal JS Mode: DOWNLOAD LINK

Simply include the code in your header, and you can begin using the library.

The code is released under the MIT license. Please refer to the end of this documentation for full details.

If you wish to report an error, please visit our github page at github.

1.3 OOP Syntax Overview

CrxOop's OOP facility offers one syntax for defining interfaces, two syntaxes for defining classes, and one syntax for everything else. The following is C++ code along with the two different equivalent syntaxes in JS that are provided by CrxOop. Apart from constants, the illustration should cover quickly most of the features supported. The last two tabs are useful for comparing between the C++ code and the JS code.

Figure 01: OOP Syntax Overview
#include <iostream>
using namespace std;
									
class InterfaceA
{
	public: virtual void interfaceAFunction(int pA) = 0;
};
class InterfaceB
{
	public: virtual void interfaceBFunction(int pA) = 0;
};
class InterfaceC : public InterfaceA, public InterfaceB
{
	public: virtual void interfaceCFunction1(int pA) = 0;
	public: virtual void interfaceCFunction2(int pA) = 0;
};
class InterfaceD
{
	public: virtual void interfaceDFunction(int pA) = 0;
};
class classA
{
	friend class classD;
									
	public: classA()
		{cout << "CONSTRUCTING A\n";}
									
	public: char publicVar[19] = "classA::publicVar\n";
	public: int publicVar2 = 5;
	public: int publicVar3[5] = {0, 0, 0, 0, 0};
	public: static char publicStaticVar[25];
	public: static void publicStaticFunction(classA* pClassA)
	{
		cout << "[START]classA::publicStaticFunction()\n";
		classA::privateStaticFunction(5);
		cout << classA::publicStaticVar;
		cout << classA::privateStaticVar;
		cout << pClassA->privateVar;
		cout << "[END]classA::publicStaticFunction()\n";
	}
	public: void publicFunction(int pA)
	{
		cout << "classA::publicFunction()\n";
	}
	public: classA* test(int pA)
	{
		cout << "[START]classA::test()\n";
		this->publicVirtualFunction(5);
		this->privateVirtualFunction(5);
		this->publicPureVirtualFunction(5);
		this->privatePureVirtualFunction(5);
		this->protectedPureVirtualFunction(5);
		this->publicFunction(5);
		this->privateFunction(5);
		classA::privateStaticFunction(5);
		cout << classA::publicStaticVar;
		cout << classA::privateStaticVar;
		cout << "[END]classA::test()\n";
		return this;
	}
	public: virtual void publicVirtualFunction(int pA)
	{
		cout << "classA::publicVirtualFunction()\n";
	}
	public: virtual void publicPureVirtualFunction(int pA) = 0;
									
	private: char privateVar[20] = "classA::privateVar\n";
	private: static char privateStaticVar[26];
	private: static void privateStaticFunction(int pA)
	{
		cout << "classA::privateStaticFunction()\n";
	}
	private: void privateFunction(int pA)
	{
		cout << "classA::privateFunction()\n";
	}
	private: virtual void privateVirtualFunction(int pA)
	{
		cout << "classA::privateVirtualFunction()\n";
	}
	private: virtual void privatePureVirtualFunction(int pA) = 0;
									
	protected: void protectedFunction(int pA)
	{
		cout << "classA::protectedFunction()\n";
	}
	protected: virtual void protectedVirtualFunction(int pA)
	{
		cout << "classA::protectedVirtualFunction()\n";
	}
	protected: virtual void protectedPureVirtualFunction(int pA) = 0;
									
};
char classA::publicStaticVar[25] = "classA::publicStaticVar\n";
char classA::privateStaticVar[26] = "classA::privateStaticVar\n";
class classB: public classA, public InterfaceC, public InterfaceD
{
	public: classB(int pA)
		{cout << "CONSTRUCTING B\n";}
									
	public: char publicVar[19] = "classB::publicVar\n";
									
	public: void publicFunction(int pA)
	{
		cout << "classB::publicFunction()\n";
		if(pA != 1)
			{this->publicFunction(1);}
		this->classA::publicFunction(5);
	}
	public: classB* test(int pA)
	{
		cout << "[START]classB::test()\n";
		this->publicVirtualFunction(5);
		this->publicFunction(5);
		((classA*)this)->publicFunction(5);
		cout << "[END]classB::test()\n";
		return this;
	}
	public: virtual void publicVirtualFunction(int pA)
	{
		cout << "classB::publicVirtualFunction()\n";
		this->classA::publicVirtualFunction(pA);
	}
	public: virtual void interfaceAFunction(int pA)
		{}
	public: virtual void interfaceBFunction(int pA)
		{}
	public: virtual void interfaceCFunction1(int pA)
		{}
	public: virtual void interfaceCFunction2(int pA)
		{}
	public: virtual void interfaceDFunction(int pA)
		{}
};
class classC: public classB
{
	public: classC():classB(5)
	{
		cout << "CONSTRUCTING C\n";
	}
	public: virtual void publicVirtualFunction(int pA) final
	{
		cout << "classC::publicVirtualFunction()\n";
		this->classA::publicVirtualFunction(pA);
		this->classA::publicFunction(pA);
		this->classA::publicVar2 = 6;
		this->classA::publicVar3[0] = 1;
		cout << this->classA::publicVar3[0] << "\n";
	}
									
	public: virtual void publicPureVirtualFunction(int pA)
	{
		cout << "classC::publicPureVirtualFunction()\n";
	}
	private: virtual void privatePureVirtualFunction(int pA)
	{
		cout << "classC::privatePureVirtualFunction()\n";
	}
	protected: virtual void protectedPureVirtualFunction(int pA)
	{
		cout << "classC::protectedPureVirtualFunction()\n";
	}
};
class classD
{	
	public: void test(classA* pClassA)
	{
		cout << "[START]classD::test()\n";
		cout << pClassA->privateVar;
		pClassA->privatePureVirtualFunction(5);
		pClassA->protectedFunction(5);
		cout << "[END]classD::test()\n";
	}
};
int main()
{
	classC* a = new classC();
	classD* b = new classD();
									
	a->test(5);
	((classA*)a)->test(5);
	classA::publicStaticFunction((classA*)a);
	b->test((classA*)a);

	return 0;
}
CONSTRUCTING A
CONSTRUCTING B
CONSTRUCTING C
[START]classB::test()
classC::publicVirtualFunction()
classA::publicVirtualFunction()
classA::publicFunction()
1
classB::publicFunction()
classB::publicFunction()
classA::publicFunction()
classA::publicFunction()
classA::publicFunction()
[END]classB::test()
[START]classA::test()
classC::publicVirtualFunction()
classA::publicVirtualFunction()
classA::publicFunction()
1
classA::privateVirtualFunction()
classC::publicPureVirtualFunction()
classC::privatePureVirtualFunction()
classC::protectedPureVirtualFunction()
classA::publicFunction()
classA::privateFunction()
classA::privateStaticFunction()
classA::publicStaticVar
classA::privateStaticVar
[END]classA::test()
[START]classA::publicStaticFunction()
classA::privateStaticFunction()
classA::publicStaticVar
classA::privateStaticVar
classA::privateVar
[END]classA::publicStaticFunction()
[START]classD::test()
classA::privateVar
classC::privatePureVirtualFunction()
classA::protectedFunction()
[END]classD::test()
crx_registerInterface("InterfaceA",
{
	"interfaceAFunction": 0
});
crx_registerInterface("InterfaceB",
{
	"interfaceBFunction": 0
});
crx_registerInterface("InterfaceC",
{
	INHERITS: ["InterfaceA", "InterfaceB"],
	"interfaceCFunction1": 0,
	"interfaceCFunction2": 0
});
crx_registerInterface("InterfaceD",
{
	"interfaceDFunction": 0
});
									
crx_registerClass("classA",
{
	FRIENDS: ['classD'],
									
	PUBLIC:
	{
		CONSTRUCT: function()
			{console.log("CONSTRUCTING A");},
		VARS:
		{
			"publicVar": "classA::publicVar",
			"publicVar2": 5,
			"publicVar3": [0, 0, 0, 0, 0]
		},
		STATIC:
		{
			VARS:
			{
				"publicStaticVar": "classA::publicStaticVar"
			},
			FUNCTIONS:
			{
				"publicStaticFunction": function(pClassA)
				{
					console.log("[START]classA::publicStaticFunction()");
					crx_static("classA").privateStaticFunction(5);
					console.log(crx_static("classA").publicStaticVar);
					console.log(crx_static("classA").privateStaticVar);
					console.log(pClassA.privateVar);
					console.log("[END]classA::publicStaticFunction()");
				}
			}
		},
		FUNCTIONS:
		{
			"publicFunction": function(pA)
			{
				console.log("classA::publicFunction()");
			},
			"test": function(pA)
			{
				console.log("[START]classA::test()");
				this.publicVirtualFunction(5);
				this.privateVirtualFunction(5);
				this.publicPureVirtualFunction(5);
				this.privatePureVirtualFunction(5);
				this.protectedPureVirtualFunction(5);
				this.publicFunction(5);
				this.privateFunction(5);
				this.STATIC.privateStaticFunction(5);
				console.log(this.STATIC.publicStaticVar);
				console.log(this.STATIC.privateStaticVar);
				console.log("[END]classA::test()");
				return this.THIS;
			}
		},
		VIRTUAL:
		{
			FUNCTIONS:
			{
				"publicVirtualFunction": function(pA)
				{
					console.log("classA::publicVirtualFunction()");
				},
				"publicPureVirtualFunction": 0
									
			}
		}
	},
	PRIVATE:
	{
		VARS:
		{
			"privateVar": "classA::privateVar"
		},
		STATIC:
		{
			VARS:
			{
				"privateStaticVar": "classA::privateStaticVar"
			},
			FUNCTIONS:
			{
				"privateStaticFunction": function(pA)
				{
					console.log("classA::privateStaticFunction()");
				}
			}
		},
		FUNCTIONS:
		{
			"privateFunction": 	function(pA)
			{
				console.log("classA::privateFunction()");
			}
		},
		VIRTUAL:
		{
			FUNCTIONS:
			{
				"privateVirtualFunction": function(pA)
				{
					console.log("classA::privateVirtualFunction()");
				},
				"privatePureVirtualFunction": 0
									
			}
		}
	},
	PROTECTED:
	{
		FUNCTIONS:
		{
			"protectedFunction": 	function(pA)
			{
				console.log("classA::protectedFunction()");
			}
		},
		VIRTUAL:
		{
			FUNCTIONS:
			{
				"protectedVirtualFunction": function(pA)
				{
					console.log("classA::protectedVirtualFunction()");
				},
				"protectedPureVirtualFunction": 0
									
			}
		}
	}
});
crx_registerClass("classB",
{
	IMPLEMENTS: ["InterfaceC", "InterfaceD"],
	EXTENDS: "classA",
	PUBLIC:
	{
		CONSTRUCT: function(pA)
			{console.log("CONSTRUCTING B");},
		VARS:
		{
			"publicVar": "classB::publicVar"
		},
		FUNCTIONS:
		{
			"publicFunction": function(pA)
			{
				console.log("classB::publicFunction()");
				if(pA != 1)
					{this.publicFunction(1);}
				this.PARENT.publicFunction(5);
			},
			"test": function(pA)
			{
				console.log("[START]classB::test()");
				this.publicVirtualFunction(5);
				this.publicFunction(5);
				this.CAST("classA").publicFunction(5);
				console.log("[END]classB::test()");
				return this.THIS;
			}
		},
		VIRTUAL:
		{
			FUNCTIONS:
			{
				"publicVirtualFunction": function(pA)
				{
					console.log("classB::publicVirtualFunction()");
					this.SR(null, "publicVirtualFunction", pA);
				},
				"interfaceAFunction": function(pA)
					{},
				"interfaceBFunction": function(pA)
					{},
				"interfaceCFunction1": function(pA)
					{},
				"interfaceCFunction2": function(pA)
					{},
				"interfaceDFunction": function(pA)
					{}
			}
		}
	}
});
crx_registerClass("classC",
{
	EXTENDS: "classB",
	PUBLIC:
	{
		CONSTRUCT: function()
		{
			this.PARENT.CONSTRUCT(5);
			console.log("CONSTRUCTING C");
		},
		VIRTUAL:
		{
			FINAL:
			{
				FUNCTIONS:
				{
					"publicVirtualFunction": function(pA)
					{
						console.log("classC::publicVirtualFunction()");
						this.SR("classA", "publicVirtualFunction", pA);
						this.SR("classA", "publicFunction", pA);
						this.SR("classA", "publicVar2", 6);
						this.SR("classA", "publicVar3")[0] = 1;
						console.log(this.SR("classA", "publicVar3")[0]);
					}
				}
			},
			FUNCTIONS:
			{
				"publicPureVirtualFunction": function(pA)
				{
					console.log("classC::publicPureVirtualFunction()\n");
				}
			}
		}
	},
	PRIVATE:
	{
		VIRTUAL:
		{
			FUNCTIONS:
			{
				"privatePureVirtualFunction": function(pA)
				{
					console.log("classC::privatePureVirtualFunction()");
				}
			}
		}
	},
	PROTECTED:
	{
		VIRTUAL:
		{
			FUNCTIONS:
			{
				"protectedPureVirtualFunction": function(pA)
				{
					console.log("classC::protectedPureVirtualFunction()");
				}
			}
		}
	}
});
crx_registerClass('classD',
{	
	PUBLIC:
	{
		FUNCTIONS:
		{
			"test": function(pClassA)
			{
				console.log("[START]classD::test()");
				console.log(this.O(pClassA, "classA").privateVar);
				this.O(pClassA, "classA").privatePureVirtualFunction(5);
				this.O(pClassA, "classA").protectedFunction(5);
				console.log("[END]classD::test()");
			}
		}
	}
});

var a = crx_new("classC");
var b = crx_new("classD");

a.test(5);
a.CAST("classA").test(5);
crx_static("classA").publicStaticFunction(a.CAST("classA"));
b.test(a.CAST("classA"));
CONSTRUCTING B
CONSTRUCTING A
CONSTRUCTING C
[START]classB::test()
classC::publicVirtualFunction()
classA::publicVirtualFunction()
classA::publicFunction()
1
classB::publicFunction()
classB::publicFunction()
classA::publicFunction()
classA::publicFunction()
classA::publicFunction()
[END]classB::test()
[START]classA::test()
classC::publicVirtualFunction()
classA::publicVirtualFunction()
classA::publicFunction()
1
classA::privateVirtualFunction()
classC::publicPureVirtualFunction()
classC::privatePureVirtualFunction()
classC::protectedPureVirtualFunction()
classA::publicFunction()
classA::privateFunction()
classA::privateStaticFunction()
classA::publicStaticVar
classA::privateStaticVar
[END]classA::test()
[START]classA::publicStaticFunction()
classA::privateStaticFunction()
classA::publicStaticVar
classA::privateStaticVar
classA::privateVar
[END]classA::publicStaticFunction()
[START]classD::test()
classA::privateVar
classC::privatePureVirtualFunction()
classA::protectedFunction()
[END]classD::test()
crx_registerInterface("InterfaceA",
{
	"interfaceAFunction": 0
});
crx_registerInterface("InterfaceB",
{
	"interfaceBFunction": 0
});
crx_registerInterface("InterfaceC",
{
	INHERITS: ["InterfaceA", "InterfaceB"],
	"interfaceCFunction1": 0,
	"interfaceCFunction2": 0
});
crx_registerInterface("InterfaceD",
{
	"interfaceDFunction": 0
});
									
crx_registerClass("classA",
{
	"VERBOSE": 1,
	"friends": ["classD"],
									
	"public CONSTRUCT": function()
	{
		console.log("CONSTRUCTING A");
	},
	"public var publicVar": "classA::publicVar",
	"public var publicVar2": 5,
	"public var publicVar3": [0, 0, 0, 0, 0],
	"public static var publicStaticVar": "classA::publicStaticVar",
	"public static function publicStaticFunction": function(pClassA)
	{
		console.log("[START]classA::publicStaticFunction()");
		crx_static("classA").privateStaticFunction(5);
		console.log(crx_static("classA").publicStaticVar);
		console.log(crx_static("classA").privateStaticVar);
		console.log(pClassA.privateVar);
		console.log("[END]classA::publicStaticFunction()");
	},
	"public function publicFunction": function(pA)
	{
		console.log("classA::publicFunction()");
	},
	"public function test": function(pA)
	{
		console.log("[START]classA::test()");
		this.publicVirtualFunction(5);
		this.privateVirtualFunction(5);
		this.publicPureVirtualFunction(5);
		this.privatePureVirtualFunction(5);
		this.protectedPureVirtualFunction(5);
		this.publicFunction(5);
		this.privateFunction(5);
		this.STATIC.privateStaticFunction(5);
		console.log(this.STATIC.publicStaticVar);
		console.log(this.STATIC.privateStaticVar);
		console.log("[END]classA::test()");
		return this.THIS;
	},
	"public virtual function publicVirtualFunction": function(pA)
	{
		console.log("classA::publicVirtualFunction()");
	},
	"public virtual function publicPureVirtualFunction": 0,
	"private var privateVar": "classA::privateVar",
	"private static var privateStaticVar": "classA::privateStaticVar",
	"private static function privateStaticFunction": function(pA)
	{
		console.log("classA::privateStaticFunction()");
	},
	"private function privateFunction": 	function(pA)
	{
		console.log("classA::privateFunction()");
	},
	"private virtual function privateVirtualFunction": function(pA)
	{
		console.log("classA::privateVirtualFunction()");
	},
	"private virtual function privatePureVirtualFunction": 0,
									
	"private function protectedFunction": 	function(pA)
	{
		console.log("classA::protectedFunction()");
	},
	"private virtual function protectedVirtualFunction": function(pA)
	{
		console.log("classA::protectedVirtualFunction()");
	},
	"private virtual function protectedPureVirtualFunction": 0
									
});
crx_registerClass("classB",
{
	"VERBOSE": 1,
	"implements": ["InterfaceC", "InterfaceD"],
	"extends": "classA",
	"public CONSTRUCT": function(pA)
	{
		console.log("CONSTRUCTING B");
	},
	"public var publicVar": "classB::publicVar",
	"public function publicFunction": function(pA)
	{
		console.log("classB::publicFunction()");
		if(pA != 1)
			{this.publicFunction(1);}
		this.PARENT.publicFunction(5);
	},
	"public function test": function(pA)
	{
		console.log("[START]classB::test()");
		this.publicVirtualFunction(5);
		this.publicFunction(5);
		this.CAST("classA").publicFunction(5);
		console.log("[END]classB::test()");
		return this.THIS;
	},
	"public virtual function publicVirtualFunction": function(pA)
	{
		console.log("classB::publicVirtualFunction()");
		this.SR(null, "publicVirtualFunction", pA);
	},
	"public virtual function interfaceAFunction": function(pA)
		{},
	"public virtual function interfaceBFunction": function(pA)
		{},
	"public virtual function interfaceCFunction1": function(pA)
		{},
	"public virtual function interfaceCFunction2": function(pA)
		{},
	"public virtual function interfaceDFunction": function(pA)
		{}
});
crx_registerClass("classC",
{
	"VERBOSE": 1,
	"extends": "classB",
	"public CONSTRUCT": function()
	{
		this.PARENT.CONSTRUCT(5);
		console.log("CONSTRUCTING C");
	},
	"public virtual function publicVirtualFunction": function(pA)
	{
		console.log("classC::publicVirtualFunction()");
		this.SR("classA", "publicVirtualFunction", pA);
		this.SR("classA", "publicFunction", pA);
		this.SR("classA", "publicVar2", 6);
		this.SR("classA", "publicVar3")[0] = 1;
		console.log(this.SR("classA", "publicVar3")[0]);
	},
	"public virtual function publicPureVirtualFunction": function(pA)
	{
		console.log("classC::publicPureVirtualFunction()\n");
	},
	"private virtual function privatePureVirtualFunction": function(pA)
	{
		console.log("classC::privatePureVirtualFunction()");
	},
	"protected virtual function protectedPureVirtualFunction": function(pA)
	{
		console.log("classC::protectedPureVirtualFunction()");
	}
});
									
crx_registerClass('classD',
{
	"VERBOSE": 1,
	"public function test": function(pClassA)
	{
		console.log("[START]classD::test()");
		console.log(this.O(pClassA, "classA").privateVar);
		this.O(pClassA, "classA").privatePureVirtualFunction(5);
		this.O(pClassA, "classA").protectedFunction(5);
		console.log("[END]classD::test()");
	}
});
var a = crx_new("classC");
var b = crx_new("classD");
									
a.test(5);
a.CAST("classA").test(5);
crx_static("classA").publicStaticFunction(a.CAST("classA"));
b.test(a.CAST("classA"));
CONSTRUCTING B
CONSTRUCTING A
CONSTRUCTING C
[START]classB::test()
classC::publicVirtualFunction()
classA::publicVirtualFunction()
classA::publicFunction()
1
classB::publicFunction()
classB::publicFunction()
classA::publicFunction()
classA::publicFunction()
classA::publicFunction()
[END]classB::test()
[START]classA::test()
classC::publicVirtualFunction()
classA::publicVirtualFunction()
classA::publicFunction()
1
classA::privateVirtualFunction()
classC::publicPureVirtualFunction()
classC::privatePureVirtualFunction()
classC::protectedPureVirtualFunction()
classA::publicFunction()
classA::privateFunction()
classA::privateStaticFunction()
classA::publicStaticVar
classA::privateStaticVar
[END]classA::test()
[START]classA::publicStaticFunction()
classA::privateStaticFunction()
classA::publicStaticVar
classA::privateStaticVar
classA::privateVar
[END]classA::publicStaticFunction()
[START]classD::test()
classA::privateVar
classC::privatePureVirtualFunction()
classA::protectedFunction()
[END]classD::test()

#include <iostream>
using namespace std;

                  

class InterfaceA
{
  public: virtual void interfaceAFunction(int pA) = 0;
};
                  

crx_registerInterface("InterfaceA",
{
  "interfaceAFunction": 0
});
                  

class InterfaceB
{
  public: virtual void interfaceBFunction(int pA) = 0;
};
                  

crx_registerInterface("InterfaceB",
{
  "interfaceBFunction": 0
});
                  

class InterfaceC : public InterfaceA, public InterfaceB
{
  public: virtual void interfaceCFunction1(int pA) = 0;
  public: virtual void interfaceCFunction2(int pA) = 0;
};
                  

crx_registerInterface("InterfaceC",
{
  INHERITS: ["InterfaceA", "InterfaceB"],
  "interfaceCFunction1": 0,
  "interfaceCFunction2": 0
});
                  

class InterfaceD
{
  public: virtual void interfaceDFunction(int pA) = 0;
};
                  

crx_registerInterface("InterfaceD",
{
  "interfaceDFunction": 0
});

                  

class classA
{
  friend class classD;

                  

crx_registerClass("classA",
{
  FRIENDS: ['classD'],

                  

  PUBLIC:
  {
                  

  public: classA()
    {cout << "CONSTRUCTING A\n";}

                  

    CONSTRUCT: function()
      {console.log("CONSTRUCTING A");},
                  

  public: char publicVar[19] = "classA::publicVar\n";
  public: int publicVar2 = 5;
  public: int publicVar3[5] = {0, 0, 0, 0, 0};
                  

    VARS:
    {
      "publicVar": "classA::publicVar",
      "publicVar2": 5,
      "publicVar3": [0, 0, 0, 0, 0]
    },
                  

    STATIC:
    {
                  

  public: static char publicStaticVar[25];
                  

      VARS:
      {
        "publicStaticVar": "classA::publicStaticVar"
      },
                  

  public: static void publicStaticFunction(classA* pClassA)
  {
    cout << "[START]classA::publicStaticFunction()\n";
    classA::privateStaticFunction(5);
    cout << classA::publicStaticVar;
    cout << classA::privateStaticVar;
    cout << pClassA->privateVar;
    cout << "[END]classA::publicStaticFunction()\n";
  }
                  

      FUNCTIONS:
      {
        "publicStaticFunction": function(pClassA)
        {
          console.log("[START]classA::publicStaticFunction()");
          crx_static("classA").privateStaticFunction(5);
          console.log(crx_static("classA").publicStaticVar);
          console.log(crx_static("classA").privateStaticVar);
          console.log(pClassA.privateVar);
          console.log("[END]classA::publicStaticFunction()");
        }
      }
                  

    },
    FUNCTIONS:
    {
                  

  public: void publicFunction(int pA)
  {
    cout << "classA::publicFunction()\n";
  }
                  

      "publicFunction": function(pA)
      {
        console.log("classA::publicFunction()");
      },
                  

  public: classA* test(int pA)
  {
    cout << "[START]classA::test()\n";
    this->publicVirtualFunction(5);
    this->privateVirtualFunction(5);
    this->publicPureVirtualFunction(5);
    this->privatePureVirtualFunction(5);
    this->protectedPureVirtualFunction(5);
    this->publicFunction(5);
    this->privateFunction(5);
    classA::privateStaticFunction(5);
    cout << classA::publicStaticVar;
    cout << classA::privateStaticVar;
    cout << "[END]classA::test()\n";

    return this;
  }
                  

      "test": function(pA)
      {
        console.log("[START]classA::test()");
        this.publicVirtualFunction(5);
        this.privateVirtualFunction(5);
        this.publicPureVirtualFunction(5);
        this.privatePureVirtualFunction(5);
        this.protectedPureVirtualFunction(5);
        this.publicFunction(5);
        this.privateFunction(5);
        this.STATIC.privateStaticFunction(5);
        console.log(this.STATIC.publicStaticVar);
        console.log(this.STATIC.privateStaticVar);
        console.log("[END]classA::test()");

        return this.THIS;
      }
                  

    },
    VIRTUAL:
    {
      FUNCTIONS:
      {
                  

  public: virtual void publicVirtualFunction(int pA)
  {
    cout << "classA::publicVirtualFunction()\n";
  }
                  

        "publicVirtualFunction": function(pA)
        {
          console.log("classA::publicVirtualFunction()");
        },
                  

  public: virtual void publicPureVirtualFunction(int pA) = 0;

                  

        "publicPureVirtualFunction": 0

                  

      }
    }
  },
  PRIVATE:
  {
                  

  private: char privateVar[20] = "classA::privateVar\n";
                  

    VARS:
    {
      "privateVar": "classA::privateVar"
    },
                  

    STATIC:
    {
                  

  private: static char privateStaticVar[26];
                  

      VARS:
      {
        "privateStaticVar": "classA::privateStaticVar"
      },
                  

  private: static void privateStaticFunction(int pA)
  {
    cout << "classA::privateStaticFunction()\n";
  }
                  

      FUNCTIONS:
      {
        "privateStaticFunction": function(pA)
        {
          console.log("classA::privateStaticFunction()");
        }
      }
                  

    },
                  

  private: void privateFunction(int pA)
  {
    cout << "classA::privateFunction()\n";
  }
                  

    FUNCTIONS:
    {
      "privateFunction":   function(pA)
      {
        console.log("classA::privateFunction()");
      }
    },
                  

    VIRTUAL:
    {
      FUNCTIONS:
      {
                  

  private: virtual void privateVirtualFunction(int pA)
  {
    cout << "classA::privateVirtualFunction()\n";
  }
                  

        "privateVirtualFunction": function(pA)
        {
          console.log("classA::privateVirtualFunction()");
        },
                  

  private: virtual void privatePureVirtualFunction(int pA) = 0;

                  

        "privatePureVirtualFunction": 0

                  

      }
    }
  },
  PROTECTED:
  {
                  

  protected: void protectedFunction(int pA)
  {
    cout << "classA::protectedFunction()\n";
  }
                  

    FUNCTIONS:
    {
      "protectedFunction":   function(pA)
      {
        console.log("classA::protectedFunction()");
      }
    },
                  

    VIRTUAL:
    {
      FUNCTIONS:
      {
                  

  protected: virtual void protectedVirtualFunction(int pA)
  {
    cout << "classA::protectedVirtualFunction()\n";
  }
                  

        "protectedVirtualFunction": function(pA)
        {
          console.log("classA::protectedVirtualFunction()");
        },
                  

  protected: virtual void protectedPureVirtualFunction(int pA) = 0;

                  

        "protectedPureVirtualFunction": 0

                  

};
                  

      }
    }
  }
});
                  

char classA::publicStaticVar[25] = "classA::publicStaticVar\n";
char classA::privateStaticVar[26] = "classA::privateStaticVar\n";
                  

class classB: public classA, public InterfaceC, public InterfaceD
{
                  

crx_registerClass("classB",
{
  IMPLEMENTS: ["InterfaceC", "InterfaceD"],
  EXTENDS: "classA",
                  

  PUBLIC:
  {
                  

  public: classB(int pA)
    {cout << "CONSTRUCTING B\n";}

                  

    CONSTRUCT: function(pA)
      {console.log("CONSTRUCTING B");},
                  

  public: char publicVar[19] = "classB::publicVar\n";

                  

    VARS:
    {
      "publicVar": "classB::publicVar"
    },
                  

    FUNCTIONS:
    {
                  

  public: void publicFunction(int pA)
  {
    cout << "classB::publicFunction()\n";

    if(pA != 1)
      {this->publicFunction(1);}

    this->classA::publicFunction(5);
  }
                  

      "publicFunction": function(pA)
      {
        console.log("classB::publicFunction()");

        if(pA != 1)
          {this.publicFunction(1);}

        this.PARENT.publicFunction(5);
      },
                  

  public: classB* test(int pA)
  {
    cout << "[START]classB::test()\n";
    this->publicVirtualFunction(5);
    this->publicFunction(5);
    ((classA*)this)->publicFunction(5);
    cout << "[END]classB::test()\n";

    return this;
  }
                  

      "test": function(pA)
      {
        console.log("[START]classB::test()");
        this.publicVirtualFunction(5);
        this.publicFunction(5);
        this.CAST("classA").publicFunction(5);
        console.log("[END]classB::test()");

        return this.THIS;
      }
                  

    },
                  

  public: virtual void publicVirtualFunction(int pA)
  {
    cout << "classB::publicVirtualFunction()\n";

    this->classA::publicVirtualFunction(pA);
  }
  public: virtual void interfaceAFunction(int pA)
    {}
  public: virtual void interfaceBFunction(int pA)
    {}
  public: virtual void interfaceCFunction1(int pA)
    {}
  public: virtual void interfaceCFunction2(int pA)
    {}
  public: virtual void interfaceDFunction(int pA)
    {}
                  

    VIRTUAL:
    {
      FUNCTIONS:
      {
        "publicVirtualFunction": function(pA)
        {
          console.log("classB::publicVirtualFunction()");
          this.SR(null, "publicVirtualFunction", pA);
        },
        "interfaceAFunction": function(pA)
          {},
        "interfaceBFunction": function(pA)
          {},
        "interfaceCFunction1": function(pA)
          {},
        "interfaceCFunction2": function(pA)
          {},
        "interfaceDFunction": function(pA)
          {}
      }
    }
                  

};
                  

  }
});
                  

class classC: public classB
{
                  

crx_registerClass("classC",
{
  EXTENDS: "classB",
                  

  PUBLIC:
  {
                  

  public: classC():classB(5)
  {
    cout << "CONSTRUCTING C\n";
  }
                  

    CONSTRUCT: function()
    {
      this.PARENT.CONSTRUCT(5);
      console.log("CONSTRUCTING C");
    },
                  

    VIRTUAL:
    {
                  

  public: virtual void publicVirtualFunction(int pA) final
  {
    cout << "classC::publicVirtualFunction()\n";

    this->classA::publicVirtualFunction(pA);
    this->classA::publicFunction(pA);
    this->classA::publicVar2 = 6;
    this->classA::publicVar3[0] = 1;
    cout << this->classA::publicVar3[0] << "\n";
  }

                  

      FINAL:
      {
        FUNCTIONS:
        {
          "publicVirtualFunction": function(pA)
          {
            console.log("classC::publicVirtualFunction()");

            this.SR("classA", "publicVirtualFunction", pA);
            this.SR("classA", "publicFunction", pA);
            this.SR("classA", "publicVar2", 6);
            this.SR("classA", "publicVar3")[0] = 1;
            console.log(this.SR("classA", "publicVar3")[0]);
          }
        }
      },
                  

  public: virtual void publicPureVirtualFunction(int pA)
  {
    cout << "classC::publicPureVirtualFunction()\n";
  }
                  

      FUNCTIONS:
      {
        "publicPureVirtualFunction": function(pA)
        {
          console.log("classC::publicPureVirtualFunction()\n");
        }
      }
                  

    }
  },
                  

  private: virtual void privatePureVirtualFunction(int pA)
  {
    cout << "classC::privatePureVirtualFunction()\n";
  }
                  

  PRIVATE:
  {
    VIRTUAL:
    {
      FUNCTIONS:
      {
        "privatePureVirtualFunction": function(pA)
        {
          console.log("classC::privatePureVirtualFunction()");
        }
      }
    }
  },
                  

  protected: virtual void protectedPureVirtualFunction(int pA)
  {
    cout << "classC::protectedPureVirtualFunction()\n";
  }
                  

  PROTECTED:
  {
    VIRTUAL:
    {
      FUNCTIONS:
      {
        "protectedPureVirtualFunction": function(pA)
        {
          console.log("classC::protectedPureVirtualFunction()");
        }
      }
    }
  }
                  

};
                  

});
                  

class classD
{  
  public: void test(classA* pClassA)
  {
    cout << "[START]classD::test()\n";
    cout << pClassA->privateVar;
    pClassA->privatePureVirtualFunction(5);
    pClassA->protectedFunction(5);
    cout << "[END]classD::test()\n";
  }
};
                  

crx_registerClass('classD',
{  
  PUBLIC:
  {
    FUNCTIONS:
    {
      "test": function(pClassA)
      {
        console.log("[START]classD::test()");
        console.log(this.O(pClassA, "classA").privateVar);
        this.O(pClassA, "classA").privatePureVirtualFunction(5);
        this.O(pClassA, "classA").protectedFunction(5);
        console.log("[END]classD::test()");
      }
    }
  }
});
                  

int main()
{
                  

  classC* a = new classC();
  classD* b = new classD();

                  


var a = crx_new("classC");
var b = crx_new("classD");
                  

  a->test(5);
  ((classA*)a)->test(5);
  classA::publicStaticFunction((classA*)a);
  b->test((classA*)a);
                  


a.test(5);
a.CAST("classA").test(5);
crx_static("classA").publicStaticFunction(a.CAST("classA"));
b.test(a.CAST("classA"));
                  


  return 0;
}
                  
CONSTRUCTING A
CONSTRUCTING B
CONSTRUCTING C
[START]classB::test()
classC::publicVirtualFunction()
classA::publicVirtualFunction()
classA::publicFunction()
1
classB::publicFunction()
classB::publicFunction()
classA::publicFunction()
classA::publicFunction()
classA::publicFunction()
[END]classB::test()
[START]classA::test()
classC::publicVirtualFunction()
classA::publicVirtualFunction()
classA::publicFunction()
1
classA::privateVirtualFunction()
classC::publicPureVirtualFunction()
classC::privatePureVirtualFunction()
classC::protectedPureVirtualFunction()
classA::publicFunction()
classA::privateFunction()
classA::privateStaticFunction()
classA::publicStaticVar
classA::privateStaticVar
[END]classA::test()
[START]classA::publicStaticFunction()
classA::privateStaticFunction()
classA::publicStaticVar
classA::privateStaticVar
classA::privateVar
[END]classA::publicStaticFunction()
[START]classD::test()
classA::privateVar
classC::privatePureVirtualFunction()
classA::protectedFunction()
[END]classD::test()
CONSTRUCTING B
CONSTRUCTING A
CONSTRUCTING C
[START]classB::test()
classC::publicVirtualFunction()
classA::publicVirtualFunction()
classA::publicFunction()
1
classB::publicFunction()
classB::publicFunction()
classA::publicFunction()
classA::publicFunction()
classA::publicFunction()
[END]classB::test()
[START]classA::test()
classC::publicVirtualFunction()
classA::publicVirtualFunction()
classA::publicFunction()
1
classA::privateVirtualFunction()
classC::publicPureVirtualFunction()
classC::privatePureVirtualFunction()
classC::protectedPureVirtualFunction()
classA::publicFunction()
classA::privateFunction()
classA::privateStaticFunction()
classA::publicStaticVar
classA::privateStaticVar
[END]classA::test()
[START]classA::publicStaticFunction()
classA::privateStaticFunction()
classA::publicStaticVar
classA::privateStaticVar
classA::privateVar
[END]classA::publicStaticFunction()
[START]classD::test()
classA::privateVar
classC::privatePureVirtualFunction()
classA::protectedFunction()
[END]classD::test()

#include <iostream>
using namespace std;

                  

class InterfaceA
{
  public: virtual void interfaceAFunction(int pA) = 0;
};
                  

crx_registerInterface("InterfaceA",
{
  "interfaceAFunction": 0
});
                  

class InterfaceB
{
  public: virtual void interfaceBFunction(int pA) = 0;
};
                  

crx_registerInterface("InterfaceB",
{
  "interfaceBFunction": 0
});
                  

class InterfaceC : public InterfaceA, public InterfaceB
{
  public: virtual void interfaceCFunction1(int pA) = 0;
  public: virtual void interfaceCFunction2(int pA) = 0;
};
                  

crx_registerInterface("InterfaceC",
{
  INHERITS: ["InterfaceA", "InterfaceB"],
  "interfaceCFunction1": 0,
  "interfaceCFunction2": 0
});
                  

class InterfaceD
{
  public: virtual void interfaceDFunction(int pA) = 0;
};
                  

crx_registerInterface("InterfaceD",
{
  "interfaceDFunction": 0
});

                  

class classA
{
  friend class classD;

                  

crx_registerClass("classA",
{
  "VERBOSE": 1,
  "friends": ["classD"],

                  

  public: classA()
    {cout << "CONSTRUCTING A\n";}

                  

  "public CONSTRUCT": function()
  {
    console.log("CONSTRUCTING A");
  },
                  

  public: char publicVar[19] = "classA::publicVar\n";
  public: int publicVar2 = 5;
  public: int publicVar3[5] = {0, 0, 0, 0, 0};
                  

  "public var publicVar": "classA::publicVar",
  "public var publicVar2": 5,
  "public var publicVar3": [0, 0, 0, 0, 0],
                  

  public: static char publicStaticVar[25];
                  

  "public static var publicStaticVar": "classA::publicStaticVar",
                  

  public: static void publicStaticFunction(classA* pClassA)
  {
    cout << "[START]classA::publicStaticFunction()\n";
    classA::privateStaticFunction(5);
    cout << classA::publicStaticVar;
    cout << classA::privateStaticVar;
    cout << pClassA->privateVar;
    cout << "[END]classA::publicStaticFunction()\n";
  }
                  

  "public static function publicStaticFunction": function(pClassA)
  {
    console.log("[START]classA::publicStaticFunction()");
    crx_static("classA").privateStaticFunction(5);
    console.log(crx_static("classA").publicStaticVar);
    console.log(crx_static("classA").privateStaticVar);
    console.log(pClassA.privateVar);
    console.log("[END]classA::publicStaticFunction()");
  },
                  

  public: void publicFunction(int pA)
  {
    cout << "classA::publicFunction()\n";
  }
                  

  "public function publicFunction": function(pA)
  {
    console.log("classA::publicFunction()");
  },
                  

  public: classA* test(int pA)
  {
    cout << "[START]classA::test()\n";
    this->publicVirtualFunction(5);
    this->privateVirtualFunction(5);
    this->publicPureVirtualFunction(5);
    this->privatePureVirtualFunction(5);
    this->protectedPureVirtualFunction(5);
    this->publicFunction(5);
    this->privateFunction(5);
    classA::privateStaticFunction(5);
    cout << classA::publicStaticVar;
    cout << classA::privateStaticVar;
    cout << "[END]classA::test()\n";

    return this;
  }
                  

  "public function test": function(pA)
  {
    console.log("[START]classA::test()");
    this.publicVirtualFunction(5);
    this.privateVirtualFunction(5);
    this.publicPureVirtualFunction(5);
    this.privatePureVirtualFunction(5);
    this.protectedPureVirtualFunction(5);
    this.publicFunction(5);
    this.privateFunction(5);
    this.STATIC.privateStaticFunction(5);
    console.log(this.STATIC.publicStaticVar);
    console.log(this.STATIC.privateStaticVar);
    console.log("[END]classA::test()");

    return this.THIS;
  },
                  

  public: virtual void publicVirtualFunction(int pA)
  {
    cout << "classA::publicVirtualFunction()\n";
  }
                  

  "public virtual function publicVirtualFunction": function(pA)
  {
    console.log("classA::publicVirtualFunction()");
  },
                  

  public: virtual void publicPureVirtualFunction(int pA) = 0;

                  

  "public virtual function publicPureVirtualFunction": 0,
                  

  private: char privateVar[20] = "classA::privateVar\n";
                  

  "private var privateVar": "classA::privateVar",
                  

  private: static char privateStaticVar[26];
                  

  "private static var privateStaticVar": "classA::privateStaticVar",
                  

  private: static void privateStaticFunction(int pA)
  {
    cout << "classA::privateStaticFunction()\n";
  }
                  

  "private static function privateStaticFunction": function(pA)
  {
    console.log("classA::privateStaticFunction()");
  },
                  

  private: void privateFunction(int pA)
  {
    cout << "classA::privateFunction()\n";
  }
                  

  "private function privateFunction":   function(pA)
  {
    console.log("classA::privateFunction()");
  },
                  

  private: virtual void privateVirtualFunction(int pA)
  {
    cout << "classA::privateVirtualFunction()\n";
  }
                  

  "private virtual function privateVirtualFunction": function(pA)
  {
    console.log("classA::privateVirtualFunction()");
  },
                  

  private: virtual void privatePureVirtualFunction(int pA) = 0;

                  

  "private virtual function privatePureVirtualFunction": 0,

                  

  protected: void protectedFunction(int pA)
  {
    cout << "classA::protectedFunction()\n";
  }
                  

  "private function protectedFunction":   function(pA)
  {
    console.log("classA::protectedFunction()");
  },
                  

  protected: virtual void protectedVirtualFunction(int pA)
  {
    cout << "classA::protectedVirtualFunction()\n";
  }
                  

  "private virtual function protectedVirtualFunction": function(pA)
  {
    console.log("classA::protectedVirtualFunction()");
  },
                  

  protected: virtual void protectedPureVirtualFunction(int pA) = 0;

                  

  "private virtual function protectedPureVirtualFunction": 0

                  

};
                  

});
                  

char classA::publicStaticVar[25] = "classA::publicStaticVar\n";
char classA::privateStaticVar[26] = "classA::privateStaticVar\n";
                  

class classB: public classA, public InterfaceC, public InterfaceD
{
                  

crx_registerClass("classB",
{
  "VERBOSE": 1,
  "implements": ["InterfaceC", "InterfaceD"],
  "extends": "classA",
                  

  public: classB(int pA)
    {cout << "CONSTRUCTING B\n";}

                  

  "public CONSTRUCT": function(pA)
  {
    console.log("CONSTRUCTING B");
  },
                  

  public: char publicVar[19] = "classB::publicVar\n";

                  

  "public var publicVar": "classB::publicVar",
                  

  public: void publicFunction(int pA)
  {
    cout << "classB::publicFunction()\n";

    if(pA != 1)
      {this->publicFunction(1);}

    this->classA::publicFunction(5);
  }
                  

  "public function publicFunction": function(pA)
  {
    console.log("classB::publicFunction()");

    if(pA != 1)
      {this.publicFunction(1);}

    this.PARENT.publicFunction(5);
  },
                  

  public: classB* test(int pA)
  {
    cout << "[START]classB::test()\n";
    this->publicVirtualFunction(5);
    this->publicFunction(5);
    ((classA*)this)->publicFunction(5);
    cout << "[END]classB::test()\n";

    return this;
  }
                  

  "public function test": function(pA)
  {
    console.log("[START]classB::test()");
    this.publicVirtualFunction(5);
    this.publicFunction(5);
    this.CAST("classA").publicFunction(5);
    console.log("[END]classB::test()");

    return this.THIS;
  },
                  

  public: virtual void publicVirtualFunction(int pA)
  {
    cout << "classB::publicVirtualFunction()\n";

    this->classA::publicVirtualFunction(pA);
  }
  public: virtual void interfaceAFunction(int pA)
    {}
  public: virtual void interfaceBFunction(int pA)
    {}
  public: virtual void interfaceCFunction1(int pA)
    {}
  public: virtual void interfaceCFunction2(int pA)
    {}
  public: virtual void interfaceDFunction(int pA)
    {}
                  

  "public virtual function publicVirtualFunction": function(pA)
  {
    console.log("classB::publicVirtualFunction()");
    this.SR(null, "publicVirtualFunction", pA);
  },
  "public virtual function interfaceAFunction": function(pA)
    {},
  "public virtual function interfaceBFunction": function(pA)
    {},
  "public virtual function interfaceCFunction1": function(pA)
    {},
  "public virtual function interfaceCFunction2": function(pA)
    {},
  "public virtual function interfaceDFunction": function(pA)
    {}
                  

};
                  

});
                  

class classC: public classB
{
                  

crx_registerClass("classC",
{
  "VERBOSE": 1,
  "extends": "classB",
                  

  public: classC():classB(5)
  {
    cout << "CONSTRUCTING C\n";
  }
                  

  "public CONSTRUCT": function()
  {
    this.PARENT.CONSTRUCT(5);
    console.log("CONSTRUCTING C");
  },
                  

  public: virtual void publicVirtualFunction(int pA) final
  {
    cout << "classC::publicVirtualFunction()\n";

    this->classA::publicVirtualFunction(pA);
    this->classA::publicFunction(pA);
    this->classA::publicVar2 = 6;
    this->classA::publicVar3[0] = 1;
    cout << this->classA::publicVar3[0] << "\n";
  }

                  

  "public virtual function publicVirtualFunction": function(pA)
  {
    console.log("classC::publicVirtualFunction()");

    this.SR("classA", "publicVirtualFunction", pA);
    this.SR("classA", "publicFunction", pA);
    this.SR("classA", "publicVar2", 6);
    this.SR("classA", "publicVar3")[0] = 1;
    console.log(this.SR("classA", "publicVar3")[0]);
  },
                  

  public: virtual void publicPureVirtualFunction(int pA)
  {
    cout << "classC::publicPureVirtualFunction()\n";
  }
                  

  "public virtual function publicPureVirtualFunction": function(pA)
  {
    console.log("classC::publicPureVirtualFunction()\n");
  },
                  

  private: virtual void privatePureVirtualFunction(int pA)
  {
    cout << "classC::privatePureVirtualFunction()\n";
  }
                  

  "private virtual function privatePureVirtualFunction": function(pA)
  {
    console.log("classC::privatePureVirtualFunction()");
  },
                  

  protected: virtual void protectedPureVirtualFunction(int pA)
  {
    cout << "classC::protectedPureVirtualFunction()\n";
  }
                  

  "protected virtual function protectedPureVirtualFunction": function(pA)
  {
    console.log("classC::protectedPureVirtualFunction()");
  }
                  

};
                  

});

                  

class classD
{  
  public: void test(classA* pClassA)
  {
    cout << "[START]classD::test()\n";
    cout << pClassA->privateVar;
    pClassA->privatePureVirtualFunction(5);
    pClassA->protectedFunction(5);
    cout << "[END]classD::test()\n";
  }
};
                  

crx_registerClass('classD',
{
  "VERBOSE": 1,
  "public function test": function(pClassA)
  {
    console.log("[START]classD::test()");
    console.log(this.O(pClassA, "classA").privateVar);
    this.O(pClassA, "classA").privatePureVirtualFunction(5);
    this.O(pClassA, "classA").protectedFunction(5);
    console.log("[END]classD::test()");
  }
});
                  

int main()
{
                  

  classC* a = new classC();
  classD* b = new classD();

                  

var a = crx_new("classC");
var b = crx_new("classD");

                  

  a->test(5);
  ((classA*)a)->test(5);
  classA::publicStaticFunction((classA*)a);
  b->test((classA*)a);
                  

a.test(5);
a.CAST("classA").test(5);
crx_static("classA").publicStaticFunction(a.CAST("classA"));
b.test(a.CAST("classA"));
                  


  return 0;
}
                  
CONSTRUCTING A
CONSTRUCTING B
CONSTRUCTING C
[START]classB::test()
classC::publicVirtualFunction()
classA::publicVirtualFunction()
classA::publicFunction()
1
classB::publicFunction()
classB::publicFunction()
classA::publicFunction()
classA::publicFunction()
classA::publicFunction()
[END]classB::test()
[START]classA::test()
classC::publicVirtualFunction()
classA::publicVirtualFunction()
classA::publicFunction()
1
classA::privateVirtualFunction()
classC::publicPureVirtualFunction()
classC::privatePureVirtualFunction()
classC::protectedPureVirtualFunction()
classA::publicFunction()
classA::privateFunction()
classA::privateStaticFunction()
classA::publicStaticVar
classA::privateStaticVar
[END]classA::test()
[START]classA::publicStaticFunction()
classA::privateStaticFunction()
classA::publicStaticVar
classA::privateStaticVar
classA::privateVar
[END]classA::publicStaticFunction()
[START]classD::test()
classA::privateVar
classC::privatePureVirtualFunction()
classA::protectedFunction()
[END]classD::test()
CONSTRUCTING B
CONSTRUCTING A
CONSTRUCTING C
[START]classB::test()
classC::publicVirtualFunction()
classA::publicVirtualFunction()
classA::publicFunction()
1
classB::publicFunction()
classB::publicFunction()
classA::publicFunction()
classA::publicFunction()
classA::publicFunction()
[END]classB::test()
[START]classA::test()
classC::publicVirtualFunction()
classA::publicVirtualFunction()
classA::publicFunction()
1
classA::privateVirtualFunction()
classC::publicPureVirtualFunction()
classC::privatePureVirtualFunction()
classC::protectedPureVirtualFunction()
classA::publicFunction()
classA::privateFunction()
classA::privateStaticFunction()
classA::publicStaticVar
classA::privateStaticVar
[END]classA::test()
[START]classA::publicStaticFunction()
classA::privateStaticFunction()
classA::publicStaticVar
classA::privateStaticVar
classA::privateVar
[END]classA::publicStaticFunction()
[START]classD::test()
classA::privateVar
classC::privatePureVirtualFunction()
classA::protectedFunction()
[END]classD::test()

1.3.1 Tree Syntax

Look at Figure 1, the Tree Syntax tab. There are Few things to note:

1.3.2 Verbose Syntax

Look at Figure 1, the Verbose Syntax tab. There are Few things to note:

1.3.3 Keywords

Refer back to Figure 1. CrxOop provides a number of 'keywords' to provide its functionality, most of which appear in the JS code in the figure, and can be understood by comparing the JS code to that of C++. The 'keywords' fall under three types:

  • Definition Keywords: These keywords are available in class definitions and interface definitions. In the tree syntax, they are object property names, and are case sensitive. In the verbose syntax they are part of object property names and are case insensitive apart for "VERBOSE" and "CONSTRUCT". Also note that in the verbose syntax, VARS becomes VAR, and FUNCTIONS becomes FUNCTION. Further more, but not shown in Figure 1, "CONSTS" becomes "CONST".

    The following is the list of these keywords: VERBOSE, FRIENDS, IMPLEMENTS, EXTENDS, PUBLIC, PROTECTED, PRIVATE, CONSTRUCT, VARS, FUNCTIONS, STATIC, and CONSTS.

  • Class Keywords: These keywords are used in the class instance functions, whether public, protected, private, public virtual, protected virtual, or private virtual. They are property names of actual variables and functions placed on the object pointed to by the javascript keyword "this" in the instance function. Hence they are case sensitive. These keywords, along with the native "this", and apart from "THIS", and "PARENT", must never be passed around as function parameters and function returns when accessed using "this". This rule also applies to the return of "O"(), i.e. the value returned by this.O().

    The following is the list of these keywords: O, THIS, PARENT, CONSTRUCT, SR (used to be VF in v1.0, please update your code), STATIC, CAST, and ANNUL.

    Needless to say, class keywords are not found in static functions, and although 'this' might be found there, it is either undefined or meaningless.

  • Global Keywords: These keywords are global javascript functions that can be used any where, and hence they are case sensitive.

    The following is the list of these keywords: crx_registerClass(), crx_registerInterface, crx_new and crx_static.

Keep in mind that all keywords, and typing functions which will be discussed later and are provided by CrxOop, can lead to fatal errors if used incorrectly. In other words, treat them all as actual keywords, the same way you do with native keywords in C++ and other languages. In other languages a misused keyword will either mean compilation failure, or runtime halt if the language is interpreted.

1.3.4 Summary Of Supported Features

The following is a list of the high level features supported in CrxOop, which are usually seen in C++ and other OOP languages:

1.4 POBP (Structures) Overview

Where the OOP part of CrxOop is an implementation of a pre existing paradigm, Structures are this author's vision, formalation, and generalization of prototypal inheritance and how it would work if javascript was a non scripting for rapid development, non strict, language like C++ or Java. In other words, structure are CrxOop's implementation of Prototype Based Programming, or as this author likes to call it, Prototype Object Based Programming (POBP).

Traditionaly, using plain javascript, different programmers resulted to different methods to implementing various features, such as private access, when coding prototype based data types. Each method had its own shortcoming, and non were complete. CrxOop introduces well defined fully functional implementation of POBP as this author envisions it. It should be noted that CrxOop's implementation is a super set of what traditionaly programmers envision when they apply their favorite method to code prototype based data types and the various effects that they want. CrxOop calls the prototype based data types, Structures.

1.4.1 Summary Of Supported Features

The following is a list of the high level features implemented in CrxOop for POPB, and an explanation for each.

  • Shared Public Scope (Variable): When a variable member has this scope, it is equivilant to setting the variable on 'this' in the constructor function when coding using plain javascript. Hence, the word 'shared' in the scope name. The variable is visible to all inheriting structures, and only one copy of it exists per instance. Shared public variables are supported on roughly IE9 era browsers and later.
  • Shared Public Scope (Function): When a function member has this scope, it is equivilant to setting the function on the prototype of the constructor function when coding using plain javscript. One, hence the word 'shared'. Two, there exists only one data entry for each function global to all objects of a particular prototype based data type. This would be contrary to setting the function on 'this' in the constructor function when coding using plain javascript.
  • Shared Private Scope (Variables and Functions): When a variable member, or a function member, has this scope the member can only be accessed on 'this'. If a structure 'A' defines the member 'x' with this scope, then only functions of 'A' can see 'x'. 'x' is private to 'A'. However, note the word 'shared'. If Structure 'B' inherits structure 'A', then functions of 'B' also have access to 'x'. But not only that, but also if 'A' itself inherits 'C', then functions of 'C' can also see 'x'. In other words, exactly like the shared public scope case, but only members of the structure chain of the instance may see 'x'. It should be noted, that this privacy is across instances, like in OOP. Refer to the structure keyword 'O'. Shared private variables are supported on roughly IE9 era browsers and later.
  • Private Scope (Variables and Functions): When a variable member, or a function member, has this scope the member can only be accessed on functions of the same structure. This is usualy enforced in plain javascript by declaring each such members as 'var' in the constructor function taking advantage of closures. However, unlike the plain javascript case, this privacy is across structure instances. Refer to the structure keyword 'O'.
  • Public Constructors: The public scope is supported for constructors only. The scope, and constructors act as expected when coding using plain javascript, but with the added benefit of the guaranteed default contstructor invocation like in the OOP case.
  • Multiple Inheritance.
  • Data Typing. See also Structrure Typing.
  • Iteration using the native javscript built "for in" loop mechanism. CrxOop provides HASOWN(), an equivilant to the built in hasOwnProperty().

1.4.2 POBP Syntax Overview

CrxOop's POBP facility offers two syntaxes for defining Structures, and one syntax for everything else. The following illustration should cover quickly most of the features supported, apart from iteration.

Figure 02: POBP Syntax Overview
crx_registerStructure('structureA',
{
	PUBLIC:
	{
		CONSTRUCT: function()
		{
			console.log('CONSTRUCTING A');
		}
	},
	PRIVATE:
	{
		VARS:
		{
			privateVar: "structureA::privateVar"
		},
		FUNCTIONS:
		{
			privateFunction: function()
			{
				return "structureA::privateFunction()";
			}
		}
	},
	SHARED:
	{
		PUBLIC:
		{
			VARS:
			{
				sharedPublicVar1: "structureA::sharedPublicVar1"
			},
			FUNCTIONS:
			{
				sharedPublicFunction1: function(pA)
				{
					console.log("I am structureA::sharedPublicFunction1() and I " +
							"recieved parameter " + pA + " and I see " + 
							this.privateVar + " and " + this.privateFunction() + " and " +
							this.sharedPublicVar1 + " and " + this.sharedPublicVar2 + " and " +
							this.sharedPrivateVar1);
					console.log("---------");
				}
			}
		},
		PRIVATE:
		{
			VARS:
			{
				sharedPrivateVar1: "structureA::sharedPrivateVar1"
			},
			FUNCTIONS:
			{
				sharedPrivateFunction1: function(pA)
				{
					console.log("I am structureA::sharedPrivateFunction1() and I " +
							"recieved parameter " + pA + " and I see " + 
							this.privateVar + " and " + this.privateFunction() + " and " +
							this.sharedPublicVar1 + " and " + this.sharedPublicVar2 + " and " +
							this.sharedPrivateVar1);
					console.log("---------");
				},
				sharedPrivateFunction2: function(pA)
				{
					console.log("I am structureA::sharedPrivateFunction2() and I " +
							"recieved parameter " + pA + " and I see " + 
							this.privateVar + " and " + this.privateFunction() + " and " +
							this.sharedPublicVar1 + " and " + this.sharedPublicVar2 + " and " +
							this.sharedPrivateVar1);
					console.log("---------");
				}
			}
		}
	}
});
crx_registerStructure('structureB',
{
	PUBLIC:
	{
		CONSTRUCT: function()
		{
			console.log('CONSTRUCTING B');
		}
	},
	PRIVATE:
	{
		VARS:
		{
			privateVar: "structureB::privateVar"
		},
		FUNCTIONS:
		{
			privateFunction: function()
			{
				return "structureB::privateFunction()";
			}
		}
	},
	SHARED:
	{
		PUBLIC:
		{
			VARS:
			{
				sharedPublicVar2: "structureB::sharedPublicVar2"
			},
			FUNCTIONS:
			{
				test2: function(pStructureB_Or_C_Or_D)
				{
					console.log("I am structureB::test2() and I can see the private shared " + 
							"variable, that I defined, in another instance. The value is " + 
							this.O(pStructureB_Or_C_Or_D).sharedPrivateVar1);
					console.log("---------");
				}
			}
		},
		PRIVATE:
		{
			VARS:
			{
				sharedPrivateVar1: "structureB::sharedPrivateVar1"
			},
			FUNCTIONS:
			{
				sharedPrivateFunction2: function(pA)
				{
					console.log("I am structureB::sharedPrivateFunction2() and I " +
							"recieved parameter " + pA + " and I see " + 
							this.privateVar + " and " + this.privateFunction() + " and " +
							this.sharedPublicVar1 + " and " + this.sharedPublicVar2 + " and " +
							this.sharedPrivateVar1);
					console.log("---------");
				}
			}
		}
	}
});
crx_registerStructure('structureC',
{
	INHERITS: ['structureB'],
	PUBLIC:
	{
		CONSTRUCT: function(pA)
		{
			console.log('CONSTRUCTING C: Passed ' + pA);
		}
	},
	PRIVATE:
	{
		VARS:
		{
			privateVar: "structureC::privateVar"
		},
		FUNCTIONS:
		{
			privateFunction: function()
			{
				return "structureC::privateFunction()";
			}
		}
	},
	SHARED:
	{
		PUBLIC:
		{
			VARS:
			{
				sharedPublicVar1: "structureC::sharedPublicVar1"
			}
		},
		PRIVATE:
		{
			VARS:
			{
				sharedPrivateVar1: "structureC::sharedPrivateVar1"
			}
		}
	}
});
crx_registerStructure('structureD',
{
	INHERITS: ['structureA', 'structureC'],
	PUBLIC:
	{
		CONSTRUCT: function(pA)
		{
			this.CONSTRUCT('structureC')(pA);
			console.log('CONSTRUCTING D');
		}
	},
	PRIVATE:
	{
		VARS:
		{
			privateVar: "structureD::privateVar"
		},
		FUNCTIONS:
		{
			privateFunction: function()
			{
				return "structureD::privateFunction()";
			}
		}
	},
	SHARED:
	{
		PUBLIC:
		{
			FUNCTIONS:
			{
				sharedPublicFunction1: function(pA)
				{
					console.log("I am structureD::sharedPublicFunction1() and I " +
							"recieved parameter " + pA + " and I see " + 
							this.privateVar + " and " + this.privateFunction() + " and " +
							this.sharedPublicVar1 + " and " + this.sharedPublicVar2 + " and " +
							this.sharedPrivateVar1);
					console.log("---------");
				},
				test: function()
				{
					this.sharedPrivateFunction1(21);
					this.sharedPrivateFunction2(24);
					this.SR("structureA", "sharedPrivateFunction1", 28);
				}
			}
		},
		PRIVATE:
		{
			VARS:
			{
				sharedPrivateVar1: "structureD::sharedPrivateVar1"
			},
			FUNCTIONS:
			{
				sharedPrivateFunction1: function(pA)
				{
					console.log("I am structureD::sharedPrivateFunction1() and I " +
							"recieved parameter " + pA + " and I see " + 
							this.privateVar + " and " + this.privateFunction() + " and " +
							this.sharedPublicVar1 + " and " + this.sharedPublicVar2 + " and " +
							this.sharedPrivateVar1);
					console.log("---------");
				}
			}
		}
	}
});

var a = crx_new("structureD", 123);

a.sharedPublicFunction1(10);
a.SR("structureA", "sharedPublicFunction1", 11);
a.test();
a.test2(crx_new("structureC", 99));
CONSTRUCTING C: Passed 123
CONSTRUCTING B
CONSTRUCTING D
CONSTRUCTING A
I am structureD::sharedPublicFunction1() and I recieved parameter 10 and I see structureD::privateVar and structureD::privateFunction() and structureC::sharedPublicVar1 and structureB::sharedPublicVar2 and structureD::sharedPrivateVar1
---------
I am structureA::sharedPublicFunction1() and I recieved parameter 11 and I see structureA::privateVar and structureA::privateFunction() and structureC::sharedPublicVar1 and structureB::sharedPublicVar2 and structureD::sharedPrivateVar1
---------
I am structureD::sharedPrivateFunction1() and I recieved parameter 21 and I see structureD::privateVar and structureD::privateFunction() and structureC::sharedPublicVar1 and structureB::sharedPublicVar2 and structureD::sharedPrivateVar1
---------
I am structureB::sharedPrivateFunction2() and I recieved parameter 24 and I see structureB::privateVar and structureB::privateFunction() and structureC::sharedPublicVar1 and structureB::sharedPublicVar2 and structureD::sharedPrivateVar1
---------
I am structureA::sharedPrivateFunction1() and I recieved parameter 28 and I see structureA::privateVar and structureA::privateFunction() and structureC::sharedPublicVar1 and structureB::sharedPublicVar2 and structureD::sharedPrivateVar1
---------
CONSTRUCTING C: Passed 99
CONSTRUCTING B
I am structureB::test2() and I can see the private shared variable, that I defined, in another instance. The value is structureC::sharedPrivateVar1
---------
crx_registerStructure('structureA',
{
	VERBOSE: 1,
	"public CONSTRUCT": function()
	{
		console.log('CONSTRUCTING A');
	},
	"private var privateVar": "structureA::privateVar",
	"private function privateFunction": function()
	{
		return "structureA::privateFunction()";
	},
	"shared public var sharedPublicVar1": "structureA::sharedPublicVar1",
	"shared public function sharedPublicFunction1": function(pA)
	{
		console.log("I am structureA::sharedPublicFunction1() and I " +
				"recieved parameter " + pA + " and I see " + 
				this.privateVar + " and " + this.privateFunction() + " and " +
				this.sharedPublicVar1 + " and " + this.sharedPublicVar2 + " and " +
				this.sharedPrivateVar1);
		console.log("---------");
	},
	"shared private var sharedPrivateVar1": "structureA::sharedPrivateVar1",
	"shared private function sharedPrivateFunction1": function(pA)
	{
		console.log("I am structureA::sharedPrivateFunction1() and I " +
				"recieved parameter " + pA + " and I see " + 
				this.privateVar + " and " + this.privateFunction() + " and " +
				this.sharedPublicVar1 + " and " + this.sharedPublicVar2 + " and " +
				this.sharedPrivateVar1);
		console.log("---------");
	},
	"shared private function sharedPrivateFunction2": function(pA)
	{
		console.log("I am structureA::sharedPrivateFunction2() and I " +
				"recieved parameter " + pA + " and I see " + 
				this.privateVar + " and " + this.privateFunction() + " and " +
				this.sharedPublicVar1 + " and " + this.sharedPublicVar2 + " and " +
				this.sharedPrivateVar1);
		console.log("---------");
	}
});
crx_registerStructure('structureB',
{
	VERBOSE: 1,
	"public CONSTRUCT": function()
	{
		console.log('CONSTRUCTING B');
	},
	"private var privateVar": "structureB::privateVar",
	"private function privateFunction": function()
	{
		return "structureB::privateFunction()";
	},
	"shared public var sharedPublicVar2": "structureB::sharedPublicVar2",
	"shared public function test2": function(pStructureB_Or_C_Or_D)
	{
		console.log("I am structureB::test2() and I can see the private shared " + 
				"variable, that I defined, in another instance. The value is " + 
				this.O(pStructureB_Or_C_Or_D).sharedPrivateVar1);
		console.log("---------");
	},
	"shared private var sharedPrivateVar1": "structureB::sharedPrivateVar1",
	"shared private function sharedPrivateFunction2": function(pA)
	{
		console.log("I am structureB::sharedPrivateFunction2() and I " +
				"recieved parameter " + pA + " and I see " + 
				this.privateVar + " and " + this.privateFunction() + " and " +
				this.sharedPublicVar1 + " and " + this.sharedPublicVar2 + " and " +
				this.sharedPrivateVar1);
		console.log("---------");
	}
});
crx_registerStructure('structureC',
{
	VERBOSE: 1,
	INHERITS: ['structureB'],
	"public CONSTRUCT": function(pA)
	{
		console.log('CONSTRUCTING C: Passed ' + pA);
	},
	"private var privateVar": "structureC::privateVar",
	"private function privateFunction": function()
	{
		return "structureC::privateFunction()";
	},
	"shared public var sharedPublicVar1": "structureC::sharedPublicVar1",
	"shared private var sharedPrivateVar1": "structureC::sharedPrivateVar1"
});
crx_registerStructure('structureD',
{
	VERBOSE: 1,
	INHERITS: ['structureA', 'structureC'],
	"public CONSTRUCT": function(pA)
	{
		this.CONSTRUCT('structureC')(pA);
		console.log('CONSTRUCTING D');
	},
	"private var privateVar": "structureD::privateVar",
	"private function privateFunction": function()
	{
		return "structureD::privateFunction()";
	},
	"shared public function sharedPublicFunction1": function(pA)
	{
		console.log("I am structureD::sharedPublicFunction1() and I " +
				"recieved parameter " + pA + " and I see " + 
				this.privateVar + " and " + this.privateFunction() + " and " +
				this.sharedPublicVar1 + " and " + this.sharedPublicVar2 + " and " +
				this.sharedPrivateVar1);
		console.log("---------");
	},
	"shared public function test": function()
	{
		this.sharedPrivateFunction1(21);
		this.sharedPrivateFunction2(24);
		this.SR("structureA", "sharedPrivateFunction1", 28);
	},
	"shared private var sharedPrivateVar1": "structureD::sharedPrivateVar1",
	"shared private function sharedPrivateFunction1": function(pA)
	{
		console.log("I am structureD::sharedPrivateFunction1() and I " +
				"recieved parameter " + pA + " and I see " + 
				this.privateVar + " and " + this.privateFunction() + " and " +
				this.sharedPublicVar1 + " and " + this.sharedPublicVar2 + " and " +
				this.sharedPrivateVar1);
		console.log("---------");
	}
});

var a = crx_new("structureD", 123);

a.sharedPublicFunction1(10);
a.SR("structureA", "sharedPublicFunction1", 11);
a.test();
a.test2(crx_new("structureC", 99));
CONSTRUCTING C: Passed 123
CONSTRUCTING B
CONSTRUCTING D
CONSTRUCTING A
I am structureD::sharedPublicFunction1() and I recieved parameter 10 and I see structureD::privateVar and structureD::privateFunction() and structureC::sharedPublicVar1 and structureB::sharedPublicVar2 and structureD::sharedPrivateVar1
---------
I am structureA::sharedPublicFunction1() and I recieved parameter 11 and I see structureA::privateVar and structureA::privateFunction() and structureC::sharedPublicVar1 and structureB::sharedPublicVar2 and structureD::sharedPrivateVar1
---------
I am structureD::sharedPrivateFunction1() and I recieved parameter 21 and I see structureD::privateVar and structureD::privateFunction() and structureC::sharedPublicVar1 and structureB::sharedPublicVar2 and structureD::sharedPrivateVar1
---------
I am structureB::sharedPrivateFunction2() and I recieved parameter 24 and I see structureB::privateVar and structureB::privateFunction() and structureC::sharedPublicVar1 and structureB::sharedPublicVar2 and structureD::sharedPrivateVar1
---------
I am structureA::sharedPrivateFunction1() and I recieved parameter 28 and I see structureA::privateVar and structureA::privateFunction() and structureC::sharedPublicVar1 and structureB::sharedPublicVar2 and structureD::sharedPrivateVar1
---------
CONSTRUCTING C: Passed 99
CONSTRUCTING B
I am structureB::test2() and I can see the private shared variable, that I defined, in another instance. The value is structureC::sharedPrivateVar1
---------
1.4.2.1 Tree Syntax

Look at Figure 2, the Tree Syntax tab. There are Few things to note:

1.4.2.2 Verbose Syntax

Look at Figure 2, the Verbose Syntax tab. There are Few things to note:

1.4.3 Keywords

Refer back to Figure 2. CrxOop provides a number of 'keywords' to provide its functionality, most of which appear in the JS code in the figure. The 'keywords' fall under three types:

  • Definition Keywords: These keywords are available in structure definitions. In the tree syntax, they are object property names, and are case sensitive. In the verbose syntax they are part of object property names and are case insensitive apart for "VERBOSE" and "CONSTRUCT". Also note that in the CONSTRUCT, VARS, and FUNCTIONS.
  • Structure Keywords: These keywords are used in the structure instance functions, whether shared public, shared private, public, or prviate. They are property names of actual variables and functions placed on the object pointed to by the javascript keyword "this" in the instance function. Hence they are case sensitive. These keywords, along with the native "this", and apart from "THIS" must never be passed around as function parameters and function returns when accessed using "this". This rule also applies to the return of "O"(), i.e. the value returned by this.O().

    The following is the list of these keywords: O, THIS, CONSTRUCT, SR, and HASOWN.

  • Global Keywords: These keywords are global javascript functions that can be used any where, and hence they are case sensitive.

    The following is the list of these keywords: crx_registerStructure, and crx_new.

Keep in mind that all keywords, and typing functions which will be discussed later and are provided by CrxOop, can lead to fatal errors if used incorrectly. In other words, treat them all as actual keywords, the same way you do with native keywords in C++ and other languages. In other languages a misused keyword will either mean compilation failure, or runtime halt if the language is interpreted.

1.5 Modes of Operation

The library has four modes of operation, the permutations of two mode flags, which we shall call here "mode_js" and "mode_crxOop".

Mode_js is related to whether the library is running in strict JS or not. The library comes in two versions, one that has strict Javascript mode enabled, and one that has it disabled. If you downloaded the one with strict mode enabled, Mode_js would be explicitly true and you would have the actual benefits and drawbacks of strict Javascript mode. If you download the other, however, the library might run with Mode_js as false or true depending on the browser support of certain features. But remember, if mode_js is true in this case, CrxOop would still be actually running in non strict Javascript mode. When mode_js is true, static functions are not supported. For debugging purposes, you can call the global function "crxOop.areStaticFunctionsSupported()", without any parameters, to check dynamically whether static functions are supported or not.

Mode_crxOop is related to whether the data structures representing class instances created by CrxOop are structurally protected from tampering with when possible or not. This flag is set explicitly using the function "crxOop.setStrictMode()" which takes a single argument, a boolean for the value of the flag. The function must be called before any class registration or interface registration takes place. Making the call "crxOop.setStrictMode(false)" is not recommended but we provide it because it can make significant performance difference if you are creating a lot of class instances, which should be a very rare case. Remember that this also compromises security. For that reason, we also provide the function "crxOop.areStructuresLocked()" which takes no arguments and returns true if structure integrity is protected, otherwise false. Note that not making the call "crxOop.setStrictMode(false)", or explicitly making the call "crxOop.setStrictMode(true)" does not guarantee the call "crxOop.areStructuresLocked()" to return true. Hence, for secure application you might want to consider making the call to "crxOop.areStructuresLocked()" regardless before running your secure code.

2.0 Errors and Warnings

If you are wondering why the documentation is beginning by talking about errors, the answer is because error handling during development with CrxOop is likely to be the most confusing part if not understood properly. There are two types of 'errors', Errors and Warnings. Both are reported in the console of your browser, and it is possible to have them reported else where if you so wish.

Warnings are non fatal errors. This means that you can likely continue running your application without problems. However, warnings should not be taken lightly, and are very likely to be turned to errors in the future.

Errors are fatal, and the most common are Definition Errors. When errors are encountered an exception is thrown that escapes the entirety of CrxOop without being caught. The exception is thrown for the convenience exceptions provide in unrolling the stack, but not because CrxOop expects you to catch them. Whether you catch those exceptions in your code or not, CrxOop will halt. This is done by setting an internal flag before throwing the exception.

When halted, most exposed functions of CrxOop, as well as class instance functions will stop working. When it comes to Definition Errors, the behavior is equivalent to those fatal errors caused by wrong syntax in other interpreted languages like PHP. PHP would continue running until it loads a PHP file that has a syntax error and then it halts immediately. In our case this happens mostly with definitions, not files. CrxOop will continue running until it encounters a definition with wrong 'syntax' and halts then. Note that class explicit registration, interface explicit registration, and structure explicit registration do not trigger parsing of the definitions and hence even if there are definition errors, that part of the code will continue running happily. Parsing only happens when the definition is actually required, such as a call to crx_new().

When developing, always look at your console. If you do not catch the errors thrown by the library, which you should not, and you are debugging using your browser's tools, the tool is likely to point you to the line of code that threw the error. The line of code will never be useful, and the message will only be useful to tell you what the error is about. After that you need to check the console. On the other hand you could be catching the error despite what we advised, in which case, check the console.

When it comes to definition errors you will not get an actual line of code pointing to the error, but you are likely to get the name of the class or interface with the malformed definition. For this reason, it is recommended that classes, interfaces, and structures be explicitly registered. If they are not, you will get a cryptic name of the class or interface where the error is.

2.1 crxOop.setLogger()

CrxOop provides the global function crxOop.setLogger which can be used to route the error messages elsewhere. The function itself does not throw exceptions. By default, messages are sent to the console if the browser supports it. The function must be called before any class registration or interface registration. If called later, the call will fail. If unsure, make the call right after the inclusion line of the library in your html code.

crxOop.setLogger(LoggingFunction)
LoggingFunction
Function
A function that takes in a two parameters. The first parameter, a string, contains the error message. The second parameter, a number, contains 0 if a fatal error, or 1 if a warning.
Return
Boolean
true on success, and false on failure.
Example
crxOop.setLogger(function(pMessage, pErrorType)
{
	if(pErrorType === 0)
		{console.log("Fatal Error: " + pMessage);}
	else if(pErrorType === 1)
		{console.log("Warning: " + pMessage);}

	return;
});

3.0 Classes

3.1 Instantiation

The equivalent of the C++ "new" keyword is the function crx_new(). The function is the only way to create class instances. The function provides four overrides which allow the creation of single or multiple instances. All four overrides allow the use of anonymous class definitions, a feature which can be abused very easily.

crx_new(classDefinitionOrClassName, [parameter01, parameter02, ...])
classDefinitionOrClassName
Class Definition or String
The class Definition (object) or name (string)
[parameter01, parameter02, ...]
Mixed
The parameters to be passed to the class constructor.
Return
Object
A class instance
Example
crx_registerClass("MyClass",
{
	"VERBOSE": 1,
	"public CONSTRUCT": function(pA, pB)
	{
		console.log("(" + pA + "," + pB + ")");
	}
});

var gMyClass = crx_new("MyClass", 1, 1);
(1,1)


crx_new(length, classDefinitionOrClassName, [parameter01, parameter02, ...])
length
integer
The number of instances to create
classDefinitionOrClassName
Class Definition or String
The class Definition (object) or name (string)
parameter01, parameter02, ...
Mixed
The parameters to be passed to the class constructor. The parameters are passed for each construction. Optional.
Return
Array
An array of class instances
Example
crx_registerClass("MyClass",
{
	"VERBOSE": 1,
	"public CONSTRUCT": function(pA, pB)
	{
		console.log("(" + pA + "," + pB + ")");
	}
});

var gMyClass = crx_new(2, "MyClass", 1, 1);
(1,1)
(1,1)


crx_new(length, parametersArray, classDefinitionOrClassName)
parametersArray
Array of Arrays
An an array of arrays such as parametersArray[i] is an array of parameters to be passed to the constructor of instance i; If parametersArray is shorter than the number of instances to create, the last element is used for the construction of the remaining instances.
length
integer
The number of instances to create
classDefinitionOrClassName
Class Definition or String
The class Definition (object) or name (string)
Return
Array
Returns an array of class instances
Example
crx_registerClass("MyClass",
{
	"VERBOSE": 1,
	"public CONSTRUCT": function(pA, pB)
	{
		console.log("(" + pA + "," + pB + ")");
	}
});

var gMyClass = crx_new(4, [[1,1], [2,2]], "MyClass");
(1,1)
(2,2)
(2,2)
(2,2)


crx_new(length, parametersFunction, classDefinitionOrClassName)
length
integer
The number of instances to create
parametersFunction
function
A function that takes a single integer, which is the current index of the instance created, and returns an array of parameters to be passed to the constructor.
classDefinitionOrClassName
Class Definition or String
The class Definition (object) or name (string)
Return
Array
Returns an array of class instances
Example
crx_registerClass("MyClass",
{
	"VERBOSE": 1,
	"public CONSTRUCT": function(pA, pB)
	{
		console.log("(" + pA + "," + pB + ")");
	}
});

var gMyClass = crx_new(4, function(pIndex)
{
	return [pIndex, pIndex * pIndex];
}, "MyClass");
(0,0)
(1,1)
(2,4)
(3,9)

3.2 Definition and Registration

Before class instances can be made, they must be defined using either of the two syntaxes mentioned in the introduction. Definitions must not be created or altered using calles to defineProperty(), seal() or other similar methods.

During definition write up, you will encounter syntax errors, as with every other code you write in javascript. After fixing the syntax, you will likely encounter Definition Errors, which are CrxOop's equivalent of javascript syntax errors. These must also be fixed before anything happens. Please refer to the section on errors for more information.

After a definition, a class can either be resgistered explicitly using crx_registerClass() which would allow you to give it a name, or registered implicitly during calls to other parts of the library, such as crx_new.

Explicit registration is the recommended way of registration, and you can either do it by assigning a definition to a variable and then calling crx_registerClass(). In this case instances can be created using the variable name of the definition, or the registered class name.

var classDefinition =
{
	//DEFINITION
};
crx_registerClass("myNameSpace.myClass", classDefinition);
var instance1 = crx_new(classDefinition);
//OR
var instance2 = crx_new("myNameSpace.myClass");

Or by passing the definition immediately to crx_registerClass(), which is our prefered approach:

crx_registerClass("myNameSpace.myClass",
{
	//DEFINITION
});
var instance2 = crx_new("myNameSpace.myClass");

Note that there is no actual support for name spaces. The full string "myNameSpace.myClass" is the name of the class, and not just "myClass". However the use of ".", or something similar, is useful to avoid name collisions. Also note that class names can collide with interface names and vice versa. Explicit registration is very useful when it comes to definition errors.

The following is an example of implicit registration:

var classDefinition =
{
	//DEFINITION
};
var instance1 = crx_new(classDefinition);

Note that classes registered implicitly can not be re registered explicitly later on. Also note that classes that are not registered implicitly or explicitly do not exist as far as CrxOop is concerned until they are registered. This is important to keep in mind when encountering errors about missing definitions.

With implicit registration, one can define anonymous classes

var instance1 = crx_new(
{
	//DEFINITION
});

The above can be convenient, however beware. Every such call initiates an internal class registration, which coupled with the immediate need to build an instance, leads to parsing which means resource consumption. If you need to make more than one instance of a class, do not declare it anonymously. This does not simply mean do not put the call in a loop. The call could also be in a function called multiple times for example. However, remember that you could always make multiple instances of your anonymous class using the array form of crx_new, instead of a loop, and because the call to crx_new is only made once this way, you suffer no extra resource loss.

3.2.1 crx_registerClass()

crx_registerClass(className, classDefinition)
className
String
The class name (string). It is recommended to use '.' to name space your class names, but remember CrxOop has no actual support for name spaces. If you call your class "myNameSpace.myClass", then the class is called "myNameSpace.myClass", and not "myClass".
classDefinition
Class Definition
The class definition (object)
Example
crx_registerClass("myNameSpace.myClass",
{
	//DEFINITION
});

3.3 Class Components

3.3.1 Constructor

Look at Figure 01. The function CONSTRUCT is the equivalent of a C++ constructor. Please note the following

  • The definition keyword CONSTRUCT is case sensitive even in the verbose syntax.
  • A constructor may not throw an exception. This will cause a fatal error.
  • A constructor may call the constructor of the base class, one level up, but no more. This means, for example, that the constructor of a class may not call the grand parent class's constructor
  • A default constructor is created when no constructor is defined. The default constructor is a function that takes no arguments.
  • The class constructor is called automatically with no arguments if the derived/child class constructor did not call it explicitly.
  • A constructor may not return any thing.

If you look at figure Figure 01 you will notice that the javascript code has identical output to that of the C++ code in , except for the order of construction. To understand why, and to get the same order as in C++, you need to know the steps of construction. Look at the figure below. Given a class 'C', which extends class 'B', which itself extends class 'A', as an example, construction of an instance of C follows the following steps:

  1. The instance is fully created. This means that in memory, we now have a single instance that fully contains the three images of 'C', 'B' and 'A'.
  2. The constructor of the most derived class is called first. In our example, this means the constructor of class 'C'. This is the opposite of C++ and intuition in general.
  3. After the constructor is called, CrxOop checks whether the constructor of the parent class has been called. If not, the parent class constructor is called. In our example, the parent class would be class 'B'.
  4. The process repeats until all constructors are called.
  5. The instance is now locked. This protects from new properties being created on the underlying javascript object.

Consider the following code and output:

Instance Construction
crx_registerClass("ClassA",
{
	"VERBOSE": 1,
	"public CONSTRUCT": function(pA)
	{
		console.log("CONSTRUCTING ClassA using pA = " + pA);
	}
});
crx_registerClass("ClassB",
{
	"VERBOSE": 1,
	"extends": "ClassA",
	"public CONSTRUCT": function(pA)
	{
		console.log("CONSTRUCTING ClassB using pA = " + pA);
	}
});
crx_registerClass("ClassC",
{
	"VERBOSE": 1,
	"extends": "ClassB",
	"public CONSTRUCT": function(pA)
	{
		console.log("CONSTRUCTING ClassC using pA = " + pA);
	}
});
crx_new("ClassC", 5);
CONSTRUCTING ClassC using pA = 5
CONSTRUCTING ClassB using pA = undefined
CONSTRUCTING ClassA using pA = undefined

Let us follow what happened:

  • The instance was fully created.
  • The constructor of ClassC was called with the passed in parameter, 5.
  • After the constructor of ClassC finished executing, CrxOop found that the constructor of ClassB was not called and called it without any parameters.
  • After the constructor of ClassB finished executing, CrxOop found that the constructor of ClassA was not called and called it without any parameters.
  • The new instance is now locked.

Hence to make the constructor of ClassB execute its useful code before that of the constructor of ClassC, we call it as the first line of code in the constructor of ClassC. Changing the code of ClassC to the following:

crx_registerClass("ClassA",
{

.
.
.

crx_registerClass("ClassC",
{
	"VERBOSE": 1,
	"extends": "ClassB",
	"public CONSTRUCT": function(pA)
	{
		this.PARENT.CONSTRUCT(pA);
		console.log("CONSTRUCTING ClassC using pA = " + pA);
	}
});
crx_new("ClassC", 5);
CONSTRUCTING ClassB using pA = 5
CONSTRUCTING ClassA using pA = undefined
CONSTRUCTING ClassC using pA = 5

Let us follow what happened:

  • The instance was fully created.
  • The constructor of ClassC was called with the passed in parameter, 5, but it called the constructor of ClassB in its first line of code passing the parameter 5, before its own useful code.
  • After the constructor of ClassB finished executing, CrxOop found that the constructor of ClassA was not called and called it without any parameters.
  • The constructor of ClassC resumed executing its useful code.
  • The new instance is now locked.

We are now almost there towards getting the order of construction that we want but not yet. However, one very important thing to notice is that the ancestors of ClassC fully finished executing their constructors before ClassC finished its own. This is important because it is sufficient in practice. If you are the developer of ClassC, and you want the ancestor's constructor to be called first, all you care about is the ancestors of ClassC to finish doing what they need in their constructors before your class begins executing its constructing code. The order of the construction of your ancestors would matter not. If it did, it would have been the worry of the developer of ClassB, and so forth.

We shall now do the same in ClassB, and call the constructor of Class A as the first line:

crx_registerClass("ClassA",
{

.
.
.

crx_registerClass("ClassB",
{
	"VERBOSE": 1,
	"extends": "ClassA",
	"public CONSTRUCT": function(pA)
	{
		this.PARENT.CONSTRUCT(pA)
		console.log("CONSTRUCTING ClassB using pA = " + pA);
	}
});
crx_registerClass("ClassC",
{
	"VERBOSE": 1,
	"extends": "ClassB",
	"public CONSTRUCT": function(pA)
	{
		this.PARENT.CONSTRUCT(pA);
		console.log("CONSTRUCTING ClassC using pA = " + pA);
	}
});
crx_new("ClassC", 5);
CONSTRUCTING ClassA using pA = 5
CONSTRUCTING ClassB using pA = 5
CONSTRUCTING ClassC using pA = 5

We now have the effect we usually want. Let us follow what happened:

  • The instance was fully created.
  • The constructor of ClassC was called with the passed in parameter, 5, but it called the constructor of CLassB in its first line of code passing the parameter 5, before executing its own useful code.
  • The constructor of ClassB was called with the passed in parameter, 5, but it called the constructor of CLassA in its first line of code passing the parameter 5, before executing its own useful code.
  • After the constructor of ClassA finished executing, ClassB's constructor began executing its useful code.
  • After the constructor of ClassB finished executing, ClassC's constructor began executing its useful code.
  • The constructor of ClassC finished executing its useful code.
  • The new instance is now locked.

3.3.2 Public and Private Instance Variables

Instance variables are variables available to class instances only. The library supports both public and private accessor types. Class instance variables can be accessed using "this", or the instance object. Needless to say class private instance variables can only be accessed using "this".

Example: Instance Variables
crx_registerClass("ExampleClass",
{
	PUBLIC:
	{
		VARS:
		{
			"publicVar1": 5,
			"publicVar2": 6
		}
	},
	PRIVATE:
	{
		VARS:
		{
			"privateVar1": 7,
			"privateVar2": 8
		}
	}
});
crx_registerClass("ExampleClass",
{
	"VERBOSE": 1,
	"public var publicVar1": 5,
	"public var publicVar2": 6,
	"private var privateVar1": 7,
	"private var privateVar2": 8
});

Instantiation of class instance variables can take place in the definition as shown above, however it is important to understand that the statement in the definition with the instantiation is executed only once, and further more when an instance is created the result is simply copied over using the assignment operator. Consider the following example:

Example: Instance Variables Instantiation
crx_registerClass("ExampleClass",
{
	PUBLIC:
	{
		VARS:
		{
			"publicVar": {}
		}
	}
});

var instance1 = crx_new("ExampleClass");
var instance2 = crx_new("ExampleClass");

instance1.publicVar['someProperty'] = 10;
console.log(instance2.publicVar.someProperty);
10
crx_registerClass("ExampleClass",
{
	"VERBOSE": 1,
	"public var publicVar": {}
});

var instance1 = crx_new("ExampleClass");
var instance2 = crx_new("ExampleClass");

instance1.publicVar['someProperty'] = 10;
console.log(instance2.publicVar.someProperty);
10

You might have expected the above to print "undefined", yet it printed 10. This is because both instance1.publicVar and instance2.publicVar are pointing to the same object. The statement

	"public var publicVar": {}

in the definition executed only once, and the result, a pointer to the object, was copied over for both instances. If you wanted a new object for each instance publicVar variable, you should instantiate the variable in the constructor, but the variable should still be declared in the definition.

WARNING: Assigning Javascript functions to private instance variables is dangerous and must never be done. Assigning Javascript functions to public instance variables should be safe, but can lead to unexpected results, and should also be avoided. For more information see the section on security and CrxOop_var().

3.3.3 Public, Protected, and Private Functions

There are three types of class functions. In this section we discuss the first type, which are non static non virtual functions.

Example: Public, Protected And Private Functions
crx_registerClass("ExampleClass",
{
	PUBLIC:
	{
		VARS:
		{
			"publicVar": 5,
		},
		FUNCTIONS:
		{
			"publicFunction1": function()
			{
				console.log("publicFunction1: " + this.publicVar + ", " + this.privateVar);
			},
			"publicFunction2": function()
			{
				this.privateFunction1();
				this.privateFunction2(20);

				this.protectedFunction1();
				this.protectedFunction2(21);

				return "publicFunction2";
			}
		}
	},
	PROTECTED:
	{
		FUNCTIONS:
		{
			"protectedFunction1": function()
			{
				console.log("protectedFunction1: " + this.publicVar + ", " + this.privateVar);
			},
			"protectedFunction2": function(pA)
			{
				console.log("protectedFunction2: " + pA);
			}
		}
	},
	PRIVATE:
	{
		VARS:
		{
			"privateVar": 7
		},
		FUNCTIONS:
		{
			"privateFunction1": function()
			{
				console.log("privateFunction1: " + this.publicVar + ", " + this.privateVar);
			},
			"privateFunction2": function(pA)
			{
				console.log("privateFunction2: " + pA);
			}
		}
	}
});
var instance = crx_new("ExampleClass");

instance.publicFunction1();
console.log(instance.publicFunction2());
publicFunction1: 5, 7
privateFunction1: 5, 7
privateFunction2: 20
protectedFunction1: 5, 7
protecredFunction2: 21
publicFunction2
crx_registerClass("ExampleClass",
{
	"VERBOSE": 1,
	"public var publicVar": 5,
	"public function publicFunction1": function()
	{
		console.log("publicFunction1: " + this.publicVar + ", " + this.privateVar);
	},
	"public function publicFunction2": function()
	{
		this.privateFunction1();
		this.privateFunction2(20);

		this.protectedFunction1();
		this.protectedFunction2(21);

		return "publicFunction2";
	},
	"protected function protectedFunction1": function()
	{
		console.log("protectedFunction1: " + this.publicVar + ", " + this.privateVar);
	},
	"protected function protectedFunction2": function(pA)
	{
		console.log("protectedFunction2: " + pA);
	},
	"private var privateVar": 7,
	"private function privateFunction1": function()
	{
		console.log("privateFunction1: " + this.publicVar + ", " + this.privateVar);
	},
	"private function privateFunction2": function(pA)
	{
		console.log("privateFunction2: " + pA);
	}
});
var instance = crx_new("ExampleClass");

instance.publicFunction1();
console.log(instance.publicFunction2());
publicFunction1: 5, 7
privateFunction1: 5, 7
privateFunction2: 20
protectedFunction1: 5, 7
protecredFunction2: 21
publicFunction2

Inside these functions, "this" points to the instance and can be used to access public, protected and private instance functions and virtual functions, and public and private variables as seen above. Needless to say, this is still javascript, so 'this' could end up pointing elsewhere inside these functions if it is used inside an inner function, such as an anonymous function created during the function run.

Note that "this", and other Class Keywords, are not safe to pass around as function parameters and returns. More on that later.

Note that being non virtual functions, these functions, like class instance variables, suffer from name hiding.

3.3.4 Public, Protected and Private Virtual Functions

There are three types of class functions. In this section we discuss the second type, which are non static virtual functions.

Example: Virtual Functions
crx_registerClass("ExampleClass",
{
	PUBLIC:
	{
		VIRTUAL:
		{
			FUNCTIONS:
			{
				publicVirtualFunction1: function()
				{
					console.log("publicVirtualFunction1");
				},
				publicVirtualFunction2: function()
				{
					console.log("publicVirtualFunction2");
				},
				publicPureVirtualFunction1: 0
			},
			FINAL:
			{
				FUNCTIONS:
				{
					publicVirtualFinalFunction1: function()
					{
						console.log("publicVirtualFinalFunction1");
					},
					publicVirtualFinalFunction2: function()
					{
						console.log("publicVirtualFinalFunction2");
					}
				}
			}
		}
	},
	PROTECTED:
	{
		VIRTUAL:
		{
			FUNCTIONS:
			{
				protectedVirtualFunction1: function()
				{
					console.log("protectedVirtualFunction1");
				},
				protectedVirtualFunction2: function()
				{
					console.log("protectedVirtualFunction2");
				},
				protectedPureVirtualFunction1: 0
			},
			FINAL:
			{
				FUNCTIONS:
				{
					protectedVirtualFinalFunction1: function()
					{
						console.log("protectedVirtualFinalFunction1");
					},
					protectedVirtualFunction4: function()
					{
						console.log("protectedVirtualFunction4");
					}
				}
			}
		}
	},
	PRIVATE:
	{
		VARS:
		{
			"privateVar": "ExampleClass::privateVar"
		},
		VIRTUAL:
		{
			FUNCTIONS:
			{
				privateVirtualFunction1: function()
				{
					console.log("privateVirtualFunction1");
				},
				privateVirtualFunction2: function()
				{
					console.log("privateVirtualFunction2");
				},
				privatePureVirtualFunction1: 0
			}
		}
	}
});
crx_registerClass("ExampleSubClass",
{
	EXTENDS: "ExampleClass",
	PUBLIC:
	{
		VIRTUAL:
		{
			FUNCTIONS:
			{
				publicPureVirtualFunction1: function()
				{
					console.log("ExampleSubClass::publicPureVirtualFunction1");
				}
			}
		}
	},
	PROTECTED:
	{
		VIRTUAL:
		{
			FUNCTIONS:
			{
				protectedPureVirtualFunction1: function()
				{
					console.log("ExampleSubClass::protectedPureVirtualFunction1");
				}
			}
		}
	},
	PRIVATE:
	{
		VIRTUAL:
		{
			FUNCTIONS:
			{
				privatePureVirtualFunction1: function()
				{
					console.log("ExampleSubClass::privatePureVirtualFunction1");
				}
			}
		}
	}
});

//		The following would cause CrxOop to halt, because ExampleClass contains pure
//				virtual functions, and thus can not be instatiated. This makes it an
//				abstract class.
//var gExampleClass = crx_new("ExampleClass");

//		The following would work because ExampleSubClass implements all the pure virtual
//				functions found in ExampleClass. If it did not, this would also make it
//				an abstract class.
var gExampleSubClass = crx_new("ExampleSubClass");
crx_registerClass("ExampleClass",
{
	"VERBOSE": 1,
	"public virtual function publicVirtualFunction1": function()
	{
		console.log("publicVirtualFunction1");
	},
	"public virtual function publicVirtualFunction2": function()
	{
		console.log("publicVirtualFunction2");
	},
	"public virtual function publicPureVirtualFunction1": 0,
	"public virtual final function publicVirtualFinalFunction1": function()
	{
		console.log("publicVirtualFinalFunction1");
	},
	"public virtual final function publicVirtualFinalFunction2": function()
	{
		console.log("publicVirtualFinalFunction2");
	},

	"protected virtual function protectedVirtualFunction1": function()
	{
		console.log("protectedVirtualFunction1");
	},
	"protected virtual function protectedVirtualFunction2": function()
	{
		console.log("protectedVirtualFunction2");
	},
	"protected virtual function protectedPureVirtualFunction1": 0,
	"protected virtual final function protectedVirtualFinalFunction1": function()
	{
		console.log("protectedVirtualFinalFunction1");
	},
	"protected virtual final function protectedVirtualFunction4": function()
	{
		console.log("protectedVirtualFunction4");
	},

	"private virtual function privateVirtualFunction1": function()
	{
		console.log("privateVirtualFunction1");
	},
	"private virtual function privateVirtualFunction2": function()
	{
		console.log("privateVirtualFunction2");
	},
	"private virtual function privatePureVirtualFunction1": 0
});

crx_registerClass("ExampleSubClass",
{
	VERBOSE: 1,
	EXTENDS: "ExampleClass",
	"public virtual function publicPureVirtualFunction1": function()
	{
		console.log("ExampleSubClass::publicPureVirtualFunction1");
	},
	"protected virtual function protectedPureVirtualFunction1": function()
	{
		console.log("ExampleSubClass::protectedPureVirtualFunction1");
	},
	"private virtual function privatePureVirtualFunction1": function()
	{
		console.log("ExampleSubClass::privatePureVirtualFunction1");
	}
});

//		The following would cause CrxOop to halt, because ExampleClass contains pure
//				virtual functions, and thus can not be instatiated. This makes it an
//				abstract class.
//var gExampleClass = crx_new("ExampleClass");

//		The following would work because ExampleSubClass implements all the pure virtual
//				functions found in ExampleClass. If it did not, this would also make it
//				an abstract class.
var gExampleSubClass = crx_new("ExampleSubClass");

publicVirtualFinalFunction1(), publicVirtualFinalFunction2(), protectedVirtualFinalFunction1(), and protectedVirtualFinalFunction2() above are final functions; they can not be overriden in sub classes.

Virtual final functions can not be private.

publicPureVirtualFunction1(), protectedPureVirtualFunction1() and privatePureVirtualFunction1() are pure virtual functions. Classes containing pure virtual functions are Abstract Classes, and can not be instantiated. Classes extending Abstract Classess without fully implementing their pure virtual functions are also Abstract Classes.

Pure virtual functions can not be final.

If you are not familiar with C++, or the concept of virtual functions, refering to the section on class extension might help.

It is important to note, as an example, that like C++, if a derived class was to declate a virtual function f() as private, where it was declared as public in the base class, one could still gain access to f() by casting the instance to the base class. If you are not familiar with C++, an internet search for C++, virtual functions, and the Liskov substitution principle should help.

Inside these functions, "this" points to the instance and can be used to access public, protected and private instance functions and virtual functions, and public and private variables as seen above. Needless to say, this is still javascript, so 'this' could end up pointing elsewhere inside these functions if it is used inside an inner function, such as an anonymous function created during the function run.

Note that "this", and other Class Keywords, are not safe to pass around as function parameters and returns. More on that later.

3.3.5 Public and Private Static Variables, and Constants

Static variables are shared variables among all instances of a particular class. Both public and private accessors are supported. Unlike C++, which has the scope operator "::" to access static variables, Javascript has no such thing, and CrxOop provides the global keyword 'crx_static', and the class keyword 'STATIC' to access those variables. However, unlike STATIC, crx_static can only be used to access public variables, not private ones, except when used within static funtions. Hence, inside your instance functions and virtual functions, use 'this.STATIC.somePublicOrPrivateVariable', and inside your static functions and elsewhere use crx_static instead.

Constants are static variables with constant binding. If the constant was an object, the constant would be the equivilant of a constant pointer in C++ (void * const), and not a pointer to a constant object (void const *). Accessing constants works the same way as accessing other static variables, and follow the same rules mentioned above.

Example: Public and Private Static Variables
crx_registerClass("ExampleClass",
{
	PUBLIC:
	{
		CONSTS:
		{
			"publicConstant1": 5,
			"publicConstant2": 6
		},
		STATIC:
		{
			VARS:
			{
				"publicStaticVar1": 1,
				"publicStaticVar2": 2
			}
		},
		FUNCTIONS:
		{
			"test": function()
			{
				console.log(this.STATIC.publicStaticVar2 + "," + this.STATIC.privateStaticVar2);
				console.log(this.STATIC.publicConstant2 + "," + this.STATIC.privateConstant1);
			}
		}
	},
	PRIVATE:
	{
		CONSTS:
		{
			"privateConstant1": 7,
			"privateConstant2": 8
		},
		STATIC:
		{
			VARS:
			{
				"privateStaticVar1": 3,
				"privateStaticVar2": 4
			}
		}
	}
});
var instance = crx_new("ExampleClass");

instance.test();
console.log("(" + crx_static("ExampleClass").publicStaticVar2 + ")");
console.log("(" + crx_static("ExampleClass").publicConstant1 + ")");
console.log("(" + instance.STATIC.publicStaticVar2 + ")");
console.log("(" + instance.STATIC.publicConstant1 + ")");
2,4
6,7
(2)
(5)
(2)
(5)
crx_registerClass("ExampleClass",
{
	"VERBOSE": 1,
	"public const publicConstant1": 5,
	"public const publicConstant2": 6,
	"public static var publicStaticVar1": 1,
	"public static var publicStaticVar2": 2,
	"private const privateConstant1": 7,
	"private const privateConstant2": 8,
	"private static var privateStaticVar1": 3,
	"private static var privateStaticVar2": 4,
	"public function test": function()
	{
		console.log(this.STATIC.publicStaticVar2 + "," + this.STATIC.privateStaticVar2);
		console.log(this.STATIC.publicConstant2 + "," + this.STATIC.privateConstant1);
	}
});
var instance = crx_new("ExampleClass");

instance.test();
console.log("(" + crx_static("ExampleClass").publicStaticVar2 + ")");
console.log("(" + crx_static("ExampleClass").publicConstant1 + ")");
console.log("(" + instance.STATIC.publicStaticVar2 + ")");
console.log("(" + instance.STATIC.publicConstant1 + ")");
2,4
6,7
(2)
(5)
(2)
(5)

As can be seen above, 'STATIC' can also be used on instances themselves, and gives no access to privates as expected. This usage is more performant, but less clear, and more error prone. With this usage of STATIC, the class type is the type of the instance. If you feel the need to cast the instance to the correct type before using STATIC, use crx_static instead.

Note that on older browsers where defining constant bindings is not possible, each object is provided with its own copy of the constants, increasing memory usage. Furthermore, any usage of crx_static whether to access constants or other statics can be significantly slower on those browser but only on classes that have constants. This is because CrxOop will assert the constants on these browser each time crx_static is used.

WARNING: Assigning Javascript functions to private static variables is dangerous and must never be done. Assigning Javascript functions to public static variables should be safe, but can lead to unexpected results, and should also be avoided. For more information see the section on CrxOop_var.

3.3.6 Public and Private Static Functions

There are three types of class functions. In this section we discuss the third type, which is static functions. Like static variables, static functions are shared functions among all instances of a particular class. Both public and private accessors are supported. Unlike C++, which has the scope operator "::" to access static variables and static functions, Javascript has no such thing, and CrxOop provides the global keyword 'crx_static', and the class keyword 'STATIC' to access those variables and functions. However, since a valid "this" is not available inside static functions, only crx_static can be used inside static functions. Hence, inside your static functions use crx_static("className") instead. When crx_static is used inside a static function of the same class, the return of crx_static must never be passed around to or from functions. This is because in this case the return would allow access to the static private memory sector, and protected methods.

Example: Public and Private Static Functions
crx_registerClass("ExampleClass",
{
	PUBLIC:
	{
		STATIC:
		{
			VARS:
			{
				"publicStaticVar": 1
			},
			FUNCTIONS:
			{
				"test": function(pExampleClass)
				{
					//	Being called within a static function belonging to ExampleClass, while
					//		called to access statics of said class, crx_static will give
					//		access to the static private memory sector. In this situation,
					//		never pass or return the return of crx_static to or from functions.
					console.log(crx_static("ExampleClass").publicStaticVar + "," +
							crx_static("ExampleClass").privateStaticVar + ","  +
							pExampleClass.privateVar);
				}
			}
		}
	},
	PRIVATE:
	{
		VARS:
		{
			"privateVar": 5
		},
		STATIC:
		{
			VARS:
			{
				"privateStaticVar": 3
			}
		}
	}
});
var instance = crx_new("ExampleClass");

crx_static("ExampleClass").test(instance);
1,3,5
crx_registerClass("ExampleClass",
{
	"VERBOSE": 1,
	"public static var publicStaticVar": 1,
	"public static function test": function(pExampleClass)
	{
		//	Being called within a static function belonging to ExampleClass, while
		//		called to access statics of said class, crx_static will give
		//		access to the static private memory sector. In this situation,
		//		never pass or return the return of crx_static to or from functions.
		console.log(crx_static("ExampleClass").publicStaticVar + "," +
				crx_static("ExampleClass").privateStaticVar + ","  +
				pExampleClass.privateVar);
	},
	"private var privateVar": 5,
	"private static var privateStaticVar": 3
});
var instance = crx_new("ExampleClass");

crx_static("ExampleClass").test(instance);
1,3,5

Static functions are not supported on older browsers, and Safari running on older MAC OS versions. For more information see the section on CrxOop Modes. Needless to say, if you were to assign an actual function to a static variable, such as "publicStaticVar" above, as a work around, this would be equivalent to assigning a function pointer to a variable in C++, and the function would not be a class function. It would be unable to see the private and protected members of the class whether static or non static.

Note that a static function is only given access to an instance's private and protected members if the instance itself is explicitly of the same class type that the static function belongs to. It is not sufficient that an instance be castable to said type. Instead, if unsure of the type of the instance, explicitly cast it to said type before attempting access to its private and protected members in a static function.

3.4 Class Extension (aka Inheritance)

CrxOop supports single class inheritance, which will be called here class extension. Consider the following example:

crx_registerClass("ExampleClass1",
{
	PUBLIC:
	{
		VARS:
		{
			"publicVar": "I am ExampleClass1"
		},
		FUNCTIONS:
		{
			"publicFunction": function(pA)
			{
				console.log(this.publicVar);
			},
			"test": function(pA)
			{
				this.publicFunction();
				this.publicVirtualFunction();
				this.CAST("ExampleClass1").publicFunction(5);
				this.CAST("ExampleClass1").publicVirtualFunction(5);
				this.SR("ExampleClass1", "publicVirtualFunction", 5);
			}
		},
		VIRTUAL:
		{
			FUNCTIONS:
			{
				"publicVirtualFunction": function(pA)
				{
					console.log(this.publicVar);
				}
			}
		}
	}
});
crx_registerClass("ExampleClass2",
{
	EXTENDS: "ExampleClass1",
	PUBLIC:
	{
		VARS:
		{
			"publicVar": "I am ExampleClass2"
		},
		FUNCTIONS:
		{
			"publicFunction": function(pA)
			{
				console.log(this.publicVar);
			}
		},
		VIRTUAL:
		{
			FUNCTIONS:
			{
				"publicVirtualFunction": function(pA)
				{
					console.log(this.publicVar);
				}
			}
		}
	}
});

var instance = crx_new("ExampleClass2");

instance.publicFunction();
instance.publicVirtualFunction();
instance.CAST("ExampleClass1").publicFunction(5);
instance.CAST("ExampleClass1").publicVirtualFunction(5);
instance.SR("ExampleClass1", "publicVirtualFunction", 5);
console.log("------------------------------------------");
instance.test();
I am ExampleClass2
I am ExampleClass2
I am ExampleClass1
I am ExampleClass2
I am ExampleClass1
------------------------------------------
I am ExampleClass1
I am ExampleClass2
I am ExampleClass1
I am ExampleClass2
I am ExampleClass1
crx_registerClass("ExampleClass1",
{
	VERBOSE: 1,
	"public var publicVar": "I am ExampleClass1",
	"public function publicFunction": function(pA)
	{
		console.log(this.publicVar);
	},
	"public function test": function(pA)
	{
		this.publicFunction();
		this.publicVirtualFunction();
		this.CAST("ExampleClass1").publicFunction(5);
		this.CAST("ExampleClass1").publicVirtualFunction(5);
		this.SR("ExampleClass1", "publicVirtualFunction", 5);
	},
	"public virtual function publicVirtualFunction": function(pA)
	{
		console.log(this.publicVar);
	}
});
crx_registerClass("ExampleClass2",
{
	VERBOSE: 1,
	"extends": "ExampleClass1",
	"public var publicVar": "I am ExampleClass2",
	"public function publicFunction": function(pA)
	{
		console.log(this.publicVar);
	},
	"public virtual function publicVirtualFunction": function(pA)
	{
		console.log(this.publicVar);
	}
});

var instance = crx_new("ExampleClass2");

instance.publicFunction();
instance.publicVirtualFunction();
instance.CAST("ExampleClass1").publicFunction(5);
instance.CAST("ExampleClass1").publicVirtualFunction(5);
instance.SR("ExampleClass1", "publicVirtualFunction", 5);
console.log("------------------------------------------");
instance.test();
I am ExampleClass2
I am ExampleClass2
I am ExampleClass1
I am ExampleClass2
I am ExampleClass1
------------------------------------------
I am ExampleClass1
I am ExampleClass2
I am ExampleClass1
I am ExampleClass2
I am ExampleClass1

Note the following:

  • The output by doing the tests on the instance is the same by doing the test using the function 'test' in the extended class except for the first line. This is because publicFunction is a non virtual function and hence it was publicFunction in side ExampleClass1 that was called when using 'this', where publicFunction inside class ExampleClass2 was called when using the variable "instance".
  • Notice how casting affects which "publicFunction" gets called. If the call is made on the variable "instance", which is an instance of ExampleClass2, ExampleClass2::publicFunction gets called and not ExampleClass1::publicFunction due to name hiding. If however it is casted to ExampleClass1 first, then ExampleClass1::publicFunction gets called.
  • Notice how casting does not affect which "publicVirtualFunction" gets called. This is because the function is virtual, and the call will always resolve to the most derived definition.
  • Some time you might want to call a virtual function, and or other variables found up the class chain, for this, CrxOop provides the class keyword 'SR'. The above example is likely self explanatory, but please see the section on 'SR' for more information.

3.5 Class Friends

A class can declare its own friend classes giving function members of the friend classes the same access its own function members have.

crx_registerClass("ExampleClass1",
{
	PRIVATE:
	{
		VARS:
		{
			'privateVar': 'I am ExampleClass1'
		}
	},
	PROTECTED:
	{
		FUNCTIONS:
		{
			'protectedFunction': function()
			{
				console.log('protectedFunction: ' + this.privateVar);
			}
		},
		VIRTUAL:
		{
			FUNCTIONS:
			{
				'protectedVirtualFunction': function()
				{
					console.log('protectedVirtualFunction: ' + this.privateVar);
				}
			}
		}
	}
});
crx_registerClass("ExampleClass2",
{
	EXTENDS: "ExampleClass1",
	FRIENDS: ["FriendOfExampleClass2"],
	PRIVATE:
	{
		VARS:
		{
			'privateVar': 'I am ExampleClass2'
		},
		FUNCTIONS:
		{
			'privateFunction': function()
			{
				console.log('privateFunction: ' + this.privateVar);
			}
		},
		VIRTUAL:
		{
			FUNCTIONS:
			{
				'privateVirtualFunction': function()
				{
					console.log('privateVirtualFunction: ' + this.privateVar);
				}
			}
		},
		STATIC:
		{
			FUNCTIONS:
			{
				'privateStaticFunction': function(pExampleClass2)
				{
					console.log('privateStaticFunction: ' + pExampleClass2.privateVar);
					pExampleClass2.SR("ExampleClass1", "protectedFunction");
					pExampleClass2.protectedFunction();
					pExampleClass2.protectedVirtualFunction();
				}
			}
		}
	},
	PROTECTED:
	{
		FUNCTIONS:
		{
			'protectedFunction': function()
			{
				console.log('protectedFunction: ' + this.privateVar);
			}
		}
	}
});
crx_registerClass("FriendOfExampleClass2",
{
	PUBLIC:
	{
		FUNCTIONS:
		{
			'test1': function(pExampleClass2)
			{
				//	FriendOfExampleClass2 being a friend class of ExampleClass2,
				//		its instance functions can gain access to the private memory
				//	 	area of instances of ExampleClass2 using 0().
				console.log(this.O(pExampleClass2, "ExampleClass2").privateVar);
				this.O(pExampleClass2, "ExampleClass2").SR("ExampleClass1", "protectedFunction");
				this.O(pExampleClass2, "ExampleClass2").protectedFunction();
				this.O(pExampleClass2, "ExampleClass2").protectedVirtualFunction();
				
				//	Although FriendOfExampleClass2 is a friend class of ExampleClass2,
				//		its intance functions can not gain access to private static
				//		members of ExampleClass2 directly. Hence the following 
				//		would not work.
				//crx_static("ExampleClass2").privateStaticFunction(pExampleClass2);				
				
				//	Although many solutions exist for this, non of them lead to a 
				//		satisfactory syntax, and hence why there is currently no
				//		support for that in CrxOop. A work around is to gain
				//		access to the private static members of ExampleClass2
				//		using an instance of ExampleClass2.
				this.O(pExampleClass2, "ExampleClass2").STATIC.privateStaticFunction(pExampleClass2);
			}
		},
		STATIC:
		{
			FUNCTIONS:
			{
				'test2': function(pExampleClass2)
				{
					//	FriendOfExampleClass2 being a friend class of ExampleClass2,
					//		its static functions can gain access to the private memory
					//	 	area of instances of ExampleClass2. Notice how we do not
					//		need to use 'O'. This is the same if we are making the calls
					//		from static functions of ExampleClass2 itself.
					console.log(pExampleClass2.privateVar);
					pExampleClass2.SR("ExampleClass1", "protectedFunction");
					pExampleClass2.protectedFunction();
					pExampleClass2.protectedVirtualFunction();

					//	Unlike the call from the instance function, test1, the following
					//		works to give acccess to the private statics of ExampleClass2.
					//		This is the same if we are making the calls from static functions
					//		of ExampleClass2 itself.
					crx_static("ExampleClass2").privateStaticFunction(pExampleClass2);
				}
			}
		}
	}
});
instance = crx_new('FriendOfExampleClass2');

instance.test1(crx_new('ExampleClass2'));
console.log('------------------');
crx_static('FriendOfExampleClass2').test2(crx_new('ExampleClass2'));
I am ExampleClass2
protectedFunction: I am ExampleClass1
protectedFunction: I am ExampleClass2
protectedVirtualFunction: I am ExampleClass1
privateStaticFunction: I am ExampleClass2
protectedFunction: I am ExampleClass1
protectedFunction: I am ExampleClass2
protectedVirtualFunction: I am ExampleClass1
------------------
I am ExampleClass2
protectedFunction: I am ExampleClass1
protectedFunction: I am ExampleClass2
protectedVirtualFunction: I am ExampleClass1
privateStaticFunction: I am ExampleClass2
protectedFunction: I am ExampleClass1
protectedFunction: I am ExampleClass2
protectedVirtualFunction: I am ExampleClass1
crx_registerClass("ExampleClass1",
{
	VERBOSE: 1,
	'private var privateVar': 'I am ExampleClass1',
	'protected function protectedFunction': function()
	{
		console.log('protectedFunction: ' + this.privateVar);
	},
	'protected virtual function protectedVirtualFunction': function()
	{
		console.log('protectedVirtualFunction: ' + this.privateVar);
	}
});
crx_registerClass("ExampleClass2",
{
	VERBOSE: 1,
	EXTENDS: "ExampleClass1",
	FRIENDS: ["FriendOfExampleClass2"],
	'private var privateVar': 'I am ExampleClass2',
	'private function privateFunction': function()
	{
		console.log('privateFunction: ' + this.privateVar);
	},
	'private virtual function privateVirtualFunction': function()
	{
		console.log('privateVirtualFunction: ' + this.privateVar);
	},
	'private static function privateStaticFunction': function(pExampleClass2)
	{
		console.log('privateStaticFunction: ' + pExampleClass2.privateVar);
		pExampleClass2.SR("ExampleClass1", "protectedFunction");
		pExampleClass2.protectedFunction();
		pExampleClass2.protectedVirtualFunction();
	},
	'protected function protectedFunction': function()
	{
		console.log('protectedFunction: ' + this.privateVar);
	}
});
crx_registerClass("FriendOfExampleClass2",
{
	VERBOSE: 1,
	'public function test1': function(pExampleClass2)
	{
		//	FriendOfExampleClass2 being a friend class of ExampleClass2,
		//		its instance functions can gain access to the private memory
		//	 	area of instances of ExampleClass2 using 0().
		console.log(this.O(pExampleClass2, "ExampleClass2").privateVar);
		this.O(pExampleClass2, "ExampleClass2").SR("ExampleClass1", "protectedFunction");
		this.O(pExampleClass2, "ExampleClass2").protectedFunction();
		this.O(pExampleClass2, "ExampleClass2").protectedVirtualFunction();
		
		//	Although FriendOfExampleClass2 is a friend class of ExampleClass2,
		//		its intance functions can not gain access to private static
		//		members of ExampleClass2 directly. Hence the following 
		//		would not work.
		//crx_static("ExampleClass2").privateStaticFunction(pExampleClass2);				
		
		//	Although many solutions exist for this, non of them lead to a 
		//		satisfactory syntax, and hence why there is currently no
		//		support for that in CrxOop. A work around is to gain
		//		access to the private static members of ExampleClass2
		//		using an instance of ExampleClass2.
		this.O(pExampleClass2, "ExampleClass2").STATIC.privateStaticFunction(pExampleClass2);
	},
	'public static function test2': function(pExampleClass2)
	{
		//	FriendOfExampleClass2 being a friend class of ExampleClass2,
		//		its static functions can gain access to the private memory
		//	 	area of instances of ExampleClass2. Notice how we do not
		//		need to use 'O'. This is the same if we are making the calls
		//		from static functions of ExampleClass2 itself.
		console.log(pExampleClass2.privateVar);
		pExampleClass2.SR("ExampleClass1", "protectedFunction");
		pExampleClass2.protectedFunction();
		pExampleClass2.protectedVirtualFunction();

		//	Unlike the call from the instance function, test1, the following
		//		works to give acccess to the private statics of ExampleClass2.
		//		This is the same if we are making the calls from static functions
		//		of ExampleClass2 itself.
		crx_static("ExampleClass2").privateStaticFunction(pExampleClass2);
	}
});
instance = crx_new('FriendOfExampleClass2');

instance.test1(crx_new('ExampleClass2'));
console.log('------------------');
crx_static('FriendOfExampleClass2').test2(crx_new('ExampleClass2'));
I am ExampleClass2
protectedFunction: I am ExampleClass1
protectedFunction: I am ExampleClass2
protectedVirtualFunction: I am ExampleClass1
privateStaticFunction: I am ExampleClass2
protectedFunction: I am ExampleClass1
protectedFunction: I am ExampleClass2
protectedVirtualFunction: I am ExampleClass1
------------------
I am ExampleClass2
protectedFunction: I am ExampleClass1
protectedFunction: I am ExampleClass2
protectedVirtualFunction: I am ExampleClass1
privateStaticFunction: I am ExampleClass2
protectedFunction: I am ExampleClass1
protectedFunction: I am ExampleClass2
protectedVirtualFunction: I am ExampleClass1

3.6 Class Keywords

3.6.1 this, THIS

'this' and 'THIS' are perhaps the most important of the class keywords to understand. Internally, a class instance consists of a public memory sector, and a private memory sector. The 'this' keyword, which is the native Javascript 'this' keyword has access to both the public and private memory sectors. 'THIS' on the other hand has access only to the public memory sector. This is why passing 'this' to functions as parameters, or from functions as returns, is dangerous and must never be done. Instead you would pass around 'THIS'.

crx_registerClass("ExampleClass",
{
	PUBLIC:
	{
		VARS:
		{
			"publicVar": 1
		},
		FUNCTIONS:
		{
			"publicFunction": function(pExampleClass)
			{
				//	Remember 'THIS' is a class keyword and hence is found on
				//		'this' and can be accessed using either 'this':
				console.log(this.THIS.publicVar);
				//		although for clarity you should never use 'THIS' to
				//		access instance members but instead use
				console.log(this.publicVar);

				//	Or the object that is the class instance:
				console.log(pExampleClass.THIS.publicVar);
				//		but again you should never use 'THIS' to access
				//		instance members but instead use
				console.log(pExampleClass.publicVar);

				//	'THIS' is only meant for passing around. When wanting to
				//		return the instance from functions, instead of the
				//		dangerous code "return this", you would use
				return this.THIS;
			},
			"publicFunction2": function()
			{
				// 	And when wanting to pass the instance to functions,
				//		instead of passing 'this', you would pass
				// 		"this.THIS" like the following
				var vReturn = this.publicFunction(this.THIS);


				//	Imagine the danger of the following if publicFunction
				//		above returned 'this' instead of 'THIS'
				return vReturn;
			}
		}
	}
});
crx_registerClass("ExampleClass",
{
	"VERBOSE": 1,
	"public var publicVar": 1,
	"public function publicFunction": function(pExampleClass)
	{
		//	Remember 'THIS' is a class keyword and hence is found on
		//		'this' and can be accessed using either 'this':
		console.log(this.THIS.publicVar);
		//		although for clarity you should never use 'THIS' to
		//		access instance members but instead use
		console.log(this.publicVar);

		//	Or the object that is the class instance:
		console.log(pExampleClass.THIS.publicVar);
		//		but again you should never use 'THIS' to access
		//		instance members but instead use
		console.log(pExampleClass.publicVar);

		//	'THIS' is only meant for passing around. When wanting to
		//		return the instance from functions, instead of the
		//		dangerous code "return this", you would use
		return this.THIS;
	},
	"public function publicFunction2": function()
	{
		// 	And when wanting to pass the instance to functions,
		//		instead of passing 'this', you would pass
		// 		"this.THIS" like the following
		var vReturn = this.publicFunction(this.THIS);


		//	Imagine the danger of the following if publicFunction
		//		above returned 'this' instead of 'THIS'
		return vReturn;
	}
});

'this' and 'THIS' have another use in constructors, although this usage should be kept to a minimal. During construction, if you recall, the object is not locked yet and you can continue adding members to it. In the constructor, if you add members to 'this', you create private variables only, even if they are functions. If you add members to 'THIS', you create public variables only, even if they are functions. We only condone, but in rare cases, this usage to create private and public final variables (C++), or const variables (Java). For example:

crx_registerClass("ExampleClass",
{
	PUBLIC:
	{
		CONSTRUCT: function()
		{
			Object.defineProperty(this, "privateFinalVariable", {"value": 3, "writable": false});
			Object.defineProperty(this.THIS, "publicFinalVariable", {"value": 5, "writable": false});
		}
	}
});

var instance = crx_new("ExampleClass");

console.log(instance.privateFinalVariable);
console.log(instance.publicFinalVariable);
3
5
crx_registerClass("ExampleClass",
{
	"VERBOSE": 1,
	"public CONSTRUCT": function()
	{
		Object.defineProperty(this, "privateFinalVariable", {"value": 3, "writable": false});
		Object.defineProperty(this.THIS, "publicFinalVariable", {"value": 5, "writable": false});
	}
});

var instance = crx_new("ExampleClass");

console.log(instance.privateFinalVariable);
console.log(instance.publicFinalVariable);
3
5

If you look at the above, something is wrong. Global code had access to the private variable. This happened because CrxOop will optimize away the private memory sector of class instances if no private members are defined in the definition. To make the above work as expected define at least one private member, but to save memory, define a private static variable.

crx_registerClass("ExampleClass",
{
	PUBLIC:
	{
		CONSTRUCT: function()
		{
			Object.defineProperty(this, "privateFinalVariable", {"value": 3, "writable": false});
			Object.defineProperty(this.THIS, "publicFinalVariable", {"value": 5, "writable": false});
		}
	},
	PRIVATE:
	{
		STATIC:
		{
			VARS:
			{
				"dummy": 1
			}
		}
	}
});

var instance = crx_new("ExampleClass");

console.log(instance.privateFinalVariable);
console.log(instance.publicFinalVariable);
undefined
5
crx_registerClass("ExampleClass",
{
	VERBOSE: 1,
	"public CONSTRUCT": function()
	{
		Object.defineProperty(this, "privateFinalVariable", {"value": 3, "writable": false});
		Object.defineProperty(this.THIS, "publicFinalVariable", {"value": 5, "writable": false});
	},
	"private static var dummy": 1
});

var instance = crx_new("ExampleClass");

console.log(instance.privateFinalVariable);
console.log(instance.publicFinalVariable);
undefined
5

3.6.2 O

'O' is a function. While 'this' allows you access to private variables and functions, as well as publics, of the current instance, 'O' allows access to privates of other instances.

'O' also allows friend classes to access the privates of instances of classes be friending said classes. When used this way, 'O' requires two arguments.

Internally 'O' will automatically cast the object to the relevant type and return 'this' of that object. Hence 'O' must never be passed to functions as parameters or from functions as returns, and because 'O' returns 'this', the rule applies to its return too. 'O' returns null if the object can not be casted.

crx_registerClass("ExampleClass1",
{
	FRIENDS: ['FriendOfExampleClass1'],
	PUBLIC:
	{
		VARS:
		{
			"publicVar": "I am ExampleClass1's publicVar"
		},
		FUNCTIONS:
		{
			"publicFunction": function(pExampleClass1)
			{
				//	Accessing private members
				console.log(this.O(pExampleClass1).privateVar);


				//	And also publics,
				console.log(this.O(pExampleClass1).publicVar);
				//	but never do this, instead do,
				console.log(pExampleClass1.publicVar);
				//	but the above does not always work because pExampleClass1
				//		is not guaranteed to be an instance of "ExampleClass1",
				//		while 'O' automatically casts. Hence if you need
				//		casting, either continue using 'O' or do
				console.log(pExampleClass1.CAST("ExampleClass1").publicVar);


				//	Like 'this' in the previous section, 'O' must never be
				//		passed around, but this also applies to the return
				//		of 'O' which is another 'this'. For example, instead
				//		of returning this.O(pExampleClass1) to return the
				//		instance pExampleClass1, do the following
				return this.O(pExampleClass1).THIS;
			}
		}
	},
	PRIVATE:
	{
		VARS:
		{
			"privateVar": "privateVar"
		}
	}
});
crx_registerClass("ExampleClass2",
{
	EXTENDS: "ExampleClass1",
	PUBLIC:
	{
		VARS:
		{
			"publicVar": "I am ExampleClass2's publicVar"
		},
		FUNCTIONS:
		{
			"test": function()
			{
				this.publicFunction(this);
			}
		}
	}
});
crx_registerClass("FriendOfExampleClass1",
{
	PUBLIC:
	{
		FUNCTIONS:
		{
			"publicFunction": function(pExampleClass1)
			{
				//	The following is the same as done inside 
				//		ExampleClass1::publicFunction(). Notice how
				//		'O' is giving access to the friend class
				//		FriendOfExampleClass1. Also notice how in this
				//		case 'O' requires two paramters. Passing only
				//		the object will not work.
				console.log(this.O(pExampleClass1, 'ExampleClass1').privateVar);
				console.log(this.O(pExampleClass1, 'ExampleClass1').publicVar);
				console.log(pExampleClass1.publicVar);
				console.log(pExampleClass1.CAST("ExampleClass1").publicVar);
				return this.O(pExampleClass1, 'ExampleClass1').THIS;
			}
		}
	}
});

var instance = crx_new("ExampleClass2");
var instance2 = crx_new("FriendOfExampleClass1");

instance.test();
console.log('--------------------');
instance2.publicFunction(instance.CAST('ExampleClass2'));
privateVar
I am ExampleClass1's publicVar
I am ExampleClass2's publicVar
I am ExampleClass1's publicVar
--------------------
I am ExampleClass1's publicVar
I am ExampleClass2's publicVar
I am ExampleClass1's publicVar
crx_registerClass("ExampleClass1",
{
	"VERBOSE": 1,
	"FRIENDS": ['FriendOfExampleClass1'],
	"public var publicVar": "I am ExampleClass1's publicVar",
	"public function publicFunction": function(pExampleClass1)
	{
		//	Accessing private members
		console.log(this.O(pExampleClass1).privateVar);


		//	And also publics,
		console.log(this.O(pExampleClass1).publicVar);
		//	but never do this, instead do,
		console.log(pExampleClass1.publicVar);
		//	but the above does not always work because pExampleClass1
		//		is not guaranteed to be an instance of "ExampleClass1",
		//		while 'O' automatically casts. Hence if you need
		//		casting, either continue using 'O' or do
		console.log(pExampleClass1.CAST("ExampleClass1").publicVar);


		//	Like 'this' in the previous section, 'O' must never be
		//		passed around, but this also applies to the return
		//		of 'O' which is another 'this'. For example, instead
		//		of returning this.O(pExampleClass1) to return the
		//		instance pExampleClass1, do the following
		return this.O(pExampleClass1).THIS;
	},
	"private var privateVar": "privateVar"
});
crx_registerClass("ExampleClass2",
{
	"VERBOSE": 1,
	"extends": "ExampleClass1",
	"public var publicVar": "I am ExampleClass2's publicVar",
	"public function test": function()
	{
		this.publicFunction(this);
	}
});
crx_registerClass("FriendOfExampleClass1",
{
	"VERBOSE": 1,
	"public function publicFunction": function(pExampleClass1)
	{
		//	The following is the same as done inside 
		//		ExampleClass1::publicFunction(). Notice how
		//		'O' is giving access to the friend class
		//		FriendOfExampleClass1. Also notice how in this
		//		case 'O' requires two paramters. Passing only
		//		the object will not work.
		console.log(this.O(pExampleClass1, 'ExampleClass1').privateVar);
		console.log(this.O(pExampleClass1, 'ExampleClass1').publicVar);
		console.log(pExampleClass1.publicVar);
		console.log(pExampleClass1.CAST("ExampleClass1").publicVar);
		return this.O(pExampleClass1, 'ExampleClass1').THIS;
	}
});

var instance = crx_new("ExampleClass2");
var instance2 = crx_new("FriendOfExampleClass1");

instance.test();
console.log('--------------------');
instance2.publicFunction(instance.CAST('ExampleClass2'));
privateVar
I am ExampleClass1's publicVar
I am ExampleClass2's publicVar
I am ExampleClass1's publicVar
--------------------
I am ExampleClass1's publicVar
I am ExampleClass2's publicVar
I am ExampleClass1's publicVar

3.6.3 CAST

CAST is a function. It allows you to upcast or downcast an instance to other classes of the extension chain. 'CAST' must never be passed to functions as parameters or from functions as returns, and there can never be a need to do so. Misuse of this keyword can lead to fatal errors.

crx_registerClass("ExampleClass1",
{
	PUBLIC:
	{
		VARS:
		{
			"publicVar": "I am ExampleClass1's publicVar"
		},
		FUNCTIONS:
		{
			"publicFunction": function(pExampleClass2)
			{
				//	Casting to access pExampleClass2's
				//		ExampleClass1::publicVar variable
				console.log(pExampleClass2.CAST("ExampleClass1").publicVar);


				//	But casting to access ExampleClass1::privateVar
				//		will not work.
				console.log(pExampleClass2.CAST("ExampleClass1").privateVar);
				//	However, because we are in the same class that we are
				//		casting to, we can use 'O', which can be used
				//		to access publics and privates.
				console.log(this.O(pExampleClass2).privateVar);
			}
		}
	},
	PRIVATE:
	{
		VARS:
		{
			"privateVar": "privateVar"
		}
	}
});
crx_registerClass("ExampleClass2",
{
	EXTENDS: "ExampleClass1",
	PUBLIC:
	{
		VARS:
		{
			"publicVar": "I am ExampleClass2's publicVar"
		},
		FUNCTIONS:
		{
			"publicFunction": function()
			{
				console.log(this.publicVar);

				// And remember CAST is a class keyword, hence it is
				//		found on 'this' as well.
				console.log(this.CAST("ExampleClass1").publicVar);
			}
		}
	}
});

var instance = crx_new("ExampleClass2");

//	Casting to call ExampleClass1::publicFunction, but remember
//		this will not work for virtual functions.
instance.CAST("ExampleClass1").publicFunction(instance);

console.log("------------");

instance.publicFunction();
I am ExampleClass1's publicVar
undefined
privateVar
------------
I am ExampleClass2's publicVar
I am ExampleClass1's publicVar
crx_registerClass("ExampleClass1",
{
	"VERBOSE": 1,
	"public var publicVar": "I am ExampleClass1's publicVar",
	"public function publicFunction": function(pExampleClass2)
	{
		//	Casting to access pExampleClass2's
		//		ExampleClass1::publicVar variable
		console.log(pExampleClass2.CAST("ExampleClass1").publicVar);


		//	But casting to access ExampleClass1::privateVar
		//		will not work.
		console.log(pExampleClass2.CAST("ExampleClass1").privateVar);
		//	However, because we are in the same class that we are
		//		casting to, we can use 'O', which can be used
		//		to access publics and privates.
		console.log(this.O(pExampleClass2).privateVar);
	},
	"private var privateVar": "privateVar"
});
crx_registerClass("ExampleClass2",
{
	"VERBOSE": 1,
	"extends": "ExampleClass1",
	"public var publicVar": "I am ExampleClass2's publicVar",
	"public function publicFunction": function()
	{
		console.log(this.publicVar);

		// And remember CAST is a class keyword, hence it is
		//		found on 'this' as well.
		console.log(this.CAST("ExampleClass1").publicVar);
	}
});

var instance = crx_new("ExampleClass2");

//	Casting to call ExampleClass1::publicFunction, but remember
//		this will not work for virtual functions.
instance.CAST("ExampleClass1").publicFunction(instance);

console.log("------------");

instance.publicFunction();
I am ExampleClass1's publicVar
undefined
privateVar
------------
I am ExampleClass2's publicVar
I am ExampleClass1's publicVar

3.6.4 PARENT

The class keyword PARENT is equivalent to casting 'this' to the parent class using 'CAST'. However, 'PARENT' is faster than 'CAST' and more readable. 'PARENT', unlike 'this' for example, is safe to pass to and from functions.

crx_registerClass("ExampleClass1",
{
	PUBLIC:
	{
		VARS:
		{
			"publicVar": "I am ExampleClass1's publicVar"
		}
	}
});
crx_registerClass("ExampleClass2",
{
	EXTENDS: "ExampleClass1",
	PUBLIC:
	{
		VARS:
		{
			"publicVar": "I am ExampleClass2's publicVar"
		},
		FUNCTIONS:
		{
			"publicFunction": function()
			{
				console.log(this.publicVar);

				//	Printing "publicVar" of parent class
				console.log(this.PARENT.publicVar);
				//	Which is equivalent to
				console.log(this.CAST("ExampleClass1").publicVar);
			}
		}
	}
});

var instance = crx_new("ExampleClass2");

instance.publicFunction();
I am ExampleClass2's publicVar
I am ExampleClass1's publicVar
I am ExampleClass1's publicVar
crx_registerClass("ExampleClass1",
{
	"VERBOSE": 1,
	"public var publicVar": "I am ExampleClass1's publicVar"
});
crx_registerClass("ExampleClass2",
{
	"VERBOSE": 1,
	"extends": "ExampleClass1",
	"public var publicVar": "I am ExampleClass2's publicVar",
	"public function publicFunction": function()
	{
		console.log(this.publicVar);

		//	Printing "publicVar" of parent class
		console.log(this.PARENT.publicVar);
		//	Which is equivalent to
		console.log(this.CAST("ExampleClass1").publicVar);
	}
});

var instance = crx_new("ExampleClass2");

instance.publicFunction();
I am ExampleClass2's publicVar
I am ExampleClass1's publicVar
I am ExampleClass1's publicVar

3.6.5 CONSTRUCT

'CONSTRUCT' is a function. The class keyword 'CONSTRUCT' is not to be confused with the definition keyword 'CONSTRUCT'. 'CONSTRUCT' simply points to the constructor in the class definition, and is only available during construction. The only valid use of CONSTRUCT is to call the parent constructor using "this.PARENT.CONSTRUCT()" when an explicit call is needed. Never pass "CONSTRUCT" to functions as parameters, or from functions as returns.

crx_registerClass("ExampleClass1",
{
	PUBLIC:
	{
		CONSTRUCT: function(pValue)
		{
			if(pValue !== undefined)
				{this.privateVar = pValue;}
		},
		FUNCTIONS:
		{
			'test': function()
			{
				console.log(this.privateVar);
			}
		}
	},
	PRIVATE:
	{
		VARS:
		{
			"privateVar": "I am default privateVar"
		}
	}
});

crx_registerClass("ExampleClass2",
{
	EXTENDS: "ExampleClass1",
	PUBLIC:
	{
		CONSTRUCT: function(pValue)
		{
			//	Calling the parent class's constructor explicitly to
			//		pass the necessary parameters
			this.PARENT.CONSTRUCT(pValue);
		}
	}
});

crx_registerClass("ExampleClass3",
{
	EXTENDS: "ExampleClass2",
	PUBLIC:
	{
		CONSTRUCT: function(pValue)
		{
			//	Calling the parent class's constructor explicitly to
			//		pass the necessary parameters
			this.PARENT.CONSTRUCT(pValue);

			//	The following are examples of what would lead to
			//		fatal errors:
			//	- RECURSIVE CALL TO CONSTRUCTOR
			//		this.CONSTRUCT(pValue);
			//	- SECOND CALL TO PARENT's CONSTRUCTOR
			//		this.PARENT.CONSTRUCT(pValue);
			//	- CALL TO GRAND PARENT's CONSTRUCTOR
			//		this.PARENT.PARENT.CONSTRUCT(pValue);
		}
	}
});

var instance = crx_new("ExampleClass3", "I am a new privateVar");

instance.test();
I am a new privateVar
crx_registerClass("ExampleClass1",
{
	"VERBOSE": 1,
	"public CONSTRUCT": function(pValue)
	{
		if(pValue !== undefined)
			{this.privateVar = pValue;}
	},
	"public function test": function()
	{
		console.log(this.privateVar);
	},
	"private var privateVar": "I am default privateVar"
});

crx_registerClass("ExampleClass2",
{
	"VERBOSE": 1,
	"extends": "ExampleClass1",
	"public CONSTRUCT": function(pValue)
	{
		//	Calling the parent class's constructor explicitly to
		//		pass the necessary parameters
		this.PARENT.CONSTRUCT(pValue);
	}
});

crx_registerClass("ExampleClass3",
{
	"VERBOSE": 1,
	"extends": "ExampleClass2",
	"public CONSTRUCT": function(pValue)
	{
		//	Calling the parent class's constructor explicitly to
		//		pass the necessary parameters
		this.PARENT.CONSTRUCT(pValue);

		//	The following are examples of what would lead to
		//		fatal errors:
		//	- RECURSIVE CALL TO CONSTRUCTOR
		//		this.CONSTRUCT(pValue);
		//	- SECOND CALL TO PARENT's CONSTRUCTOR
		//		this.PARENT.CONSTRUCT(pValue);
		//	- CALL TO GRAND PARENT's CONSTRUCTOR
		//		this.PARENT.PARENT.CONSTRUCT(pValue);
	}
});

var instance = crx_new("ExampleClass3", "I am a new privateVar");

instance.test();
I am a new privateVar

3.6.6 STATIC

'STATIC' is used to access the static members of a class. Internally, two 'STATIC's exist, one in the private memory sector, and one in the public memory sector, and they are accessed using "this.STATIC" and "this.THIS.STATIC". Unlike "this.THIS.STATIC", "this.STATIC" is unsafe to pass to functions as parameters or from functions as returns.

crx_registerClass("ExampleClass",
{
	PUBLIC:
	{
		CONSTS:
		{
			"publicConstant": 1
		},
		STATIC:
		{
			VARS:
			{
				"publicStaticVar": 4
			}
		},
		FUNCTIONS:
		{
			'test': function()
			{
				//	Accessing a public static member and a public constant
				console.log(this.STATIC.publicStaticVar);
				console.log(this.STATIC.publicConstant);
				//	and we could have done this
				console.log(crx_static("ExampleClass").publicStaticVar);
				console.log(crx_static("ExampleClass").publicConstant);

				//	Accessing a private static member and a private constant
				console.log(this.STATIC.privateStaticVar);
				console.log(this.STATIC.privateConstant);
				//	but unlike the public accessor case, the following
				//		will not work.
				console.log(crx_static("ExampleClass").privateStaticVar);
				console.log(crx_static("ExampleClass").privateConstant);
			}
		}
	},
	PRIVATE:
	{
		CONSTS:
		{
			"privateConstant": 2
		},
		STATIC:
		{
			VARS:
			{
				"privateStaticVar": 5
			}
		}
	}
});

var instance = crx_new("ExampleClass");

instance.test();
4
1
4
1
5
2
undefined
undefined
crx_registerClass("ExampleClass",
{
	"VERBOSE": 1,
	"public const publicConstant": 1,
	"public static var publicStaticVar": 4,
	"public function test": function()
	{
		//	Accessing a public static member and a public constant
		console.log(this.STATIC.publicStaticVar);
		console.log(this.STATIC.publicConstant);
		//	and we could have done this
		console.log(crx_static("ExampleClass").publicStaticVar);
		console.log(crx_static("ExampleClass").publicConstant);

		//	Accessing a private static member and a private constant
		console.log(this.STATIC.privateStaticVar);
		console.log(this.STATIC.privateConstant);
		//	but unlike the public accessor case, the following
		//		will not work.
		console.log(crx_static("ExampleClass").privateStaticVar);
		console.log(crx_static("ExampleClass").privateConstant);
	},
	"private const privateConstant": 2,
	"private static var privateStaticVar": 5
});

var instance = crx_new("ExampleClass");

instance.test();
4
1
4
1
5
2
undefined
undefined

3.6.7 SR (Formely VF)

"VF" was a function in v1.0 of CrxOop. "VF" was there to be used to access the public virtual methods up the class extension chain. With v1.2, the added features lead to the need of generalizing VF one step further to become SR. Stands for Scope Resolver. VF is no longer available in the latest version of the library.

"SR" is roughly the equivilant of "::" in C++. However, it can only be used on instances, and up the extension chain, like "VF". But unlike "VF", it can also be used to access non virtual, non static, members of the class. Its use should generally be kept to a minimum, but is provided here for the rare need.

An important difference between "VF" and "SR", is that where "VF" would still resolve if the virtual member did not exist on the class, but still existed up the class chain, "SR" would give a fatal error. Code switching from "VF" to "SR" should not have problems as long as no such uses of "VF" exist in the code.

"SR", like "O" for example, must never be passed to functions as parameters, or returned from functions as returns. "SR" will cause fatal errors if the class is not in the instance's class extension chain at all, or if misused.

crx_registerClass("ExampleClass1",
{
	PUBLIC:
	{
		VARS:
		{
			"publicVar": "I am ExampleClass1",
		},
		VIRTUAL:
		{
			FUNCTIONS:
			{
				"pulicVirtualFunction": function(pA)
				{
					console.log(this.publicVar);
				}
			}
		},
		FUNCTIONS:
		{
			"pulicFunction": function(pA)
			{
				console.log("From ExampleClass1::publicFunction: " + this.publicVar);
			}
		}
	}
});
crx_registerClass("ExampleClass2",
{
	EXTENDS: "ExampleClass1",
	PUBLIC:
	{
		VARS:
		{
			"publicVar": "I am ExampleClass2",
		},
		VIRTUAL:
		{
			FUNCTIONS:
			{
				"pulicVirtualFunction": function(pA)
				{
					console.log(this.publicVar);
				}
			}
		},
		FUNCTIONS:
		{
			"pulicFunction": function(pA)
			{
				console.log("From ExampleClass2::publicFunction: " + this.publicVar);
			}
		}
	}
});
crx_registerClass("ExampleClass3",
{
	EXTENDS: "ExampleClass2",
	PUBLIC:
	{
		VARS:
		{
			"publicVar": "I am ExampleClass3",
		},
		VIRTUAL:
		{
			FUNCTIONS:
			{
				"pulicVirtualFunction": function(pA)
				{
					console.log(this.publicVar);
				}
			}
		},
		FUNCTIONS:
		{
			test: function(pA)
			{
				//	Calling ExampleClass1::pulicVirtualFunction
				//		using CAST will not work with virtual
				//		functions.
				this.CAST("ExampleClass1").pulicVirtualFunction(5);
				//	Instead we have to use 'SR'
				this.SR("ExampleClass1", "pulicVirtualFunction", 5);

				//	The following would call
				//		ExampleClass2::pulicVirtualFunction
				this.SR("ExampleClass2", "pulicVirtualFunction", 5);

				//	The following is equivalent to the above because 'null'
				//		automatically resolves to the parent of the instance's
				//		class, which in the case of 'this' is ExampleClass2.
				this.SR(null, "pulicVirtualFunction", 5);

				//	Trying to call ExampleClass1::pulicFunction using the following
				//		will not work because of name hiding caused by the existance
				//		of ExampleClass2::pulicFunction
				this.pulicFunction(5);
				//	Instead we have to use 'SR'
				this.SR("ExampleClass1", "pulicFunction", 5);

				//	The same problem exists for ExampleClass1::publicVar, and hence the
				//		the following will not work, reading and writing to
				//		ExampleClass2::publicVar instead.
				console.log(this.publicVar);
				this.publicVar = "FIRST CHANGE";
				//	Instead we have to use 'SR'
				console.log(this.SR("ExampleClass1", "publicVar"));
				this.SR("ExampleClass1", "publicVar", "SECOND CHANGE");

				//	Printing the result of the above
				console.log("------------------");
				console.log(this.publicVar);
				console.log(this.SR("ExampleClass2", "publicVar"));
				console.log(this.SR("ExampleClass1", "publicVar"));
			}
		}
	}
});

var instance = crx_new("ExampleClass3");

instance.test();

//SR is also available on the instance
console.log("------------------");
instance.SR("ExampleClass1", "pulicVirtualFunction", 5);
I am ExampleClass3
I am ExampleClass1
I am ExampleClass2
I am ExampleClass2
From ExampleClass2::publicFunction: I am ExampleClass2
From ExampleClass1::publicFunction: I am ExampleClass1
I am ExampleClass3
I am ExampleClass1
------------------
FIRST CHANGE
I am ExampleClass2
SECOND CHANGE
------------------
SECOND CHANGE
crx_registerClass("ExampleClass1",
{
	"VERBOSE": 1,
	"public var publicVar": "I am ExampleClass1",
	"public virtual function pulicVirtualFunction": function(pA)
	{
		console.log(this.publicVar);
	},
	"public function pulicFunction": function(pA)
	{
		console.log("From ExampleClass1::publicFunction: " + this.publicVar);
	}
});
crx_registerClass("ExampleClass2",
{
	"VERBOSE": 1,
	"extends": "ExampleClass1",
	"public var publicVar": "I am ExampleClass2",
	"public virtual function pulicVirtualFunction": function(pA)
	{
		console.log(this.publicVar);
	},
	"public function pulicFunction": function(pA)
	{
		console.log("From ExampleClass2::publicFunction: " + this.publicVar);
	}
});
crx_registerClass("ExampleClass3",
{
	"VERBOSE": 1,
	"extends": "ExampleClass2",
	"public var publicVar": "I am ExampleClass3",
	"public virtual function pulicVirtualFunction": function(pA)
	{
		console.log(this.publicVar);
	},
	"public function test": function(pA)
	{
		//	Calling ExampleClass1::pulicVirtualFunction
		//		using CAST will not work with virtual
		//		functions.
		this.CAST("ExampleClass1").pulicVirtualFunction(5);
		//	Instead we have to use 'SR'
		this.SR("ExampleClass1", "pulicVirtualFunction", 5);

		//	The following would call
		//		ExampleClass2::pulicVirtualFunction
		this.SR("ExampleClass2", "pulicVirtualFunction", 5);

		//	The following is equivalent to the above because 'null'
		//		automatically resolves to the parent of the instance's
		//		class, which in the case of 'this' is ExampleClass2.
		this.SR(null, "pulicVirtualFunction", 5);

		//	Trying to call ExampleClass1::pulicFunction using the following
		//		will not work because of name hiding caused by the existance
		//		of ExampleClass2::pulicFunction
		this.pulicFunction(5);
		//	Instead we have to use 'SR'
		this.SR("ExampleClass1", "pulicFunction", 5);

		//	The same problem exists for ExampleClass1::publicVar, and hence the
		//		the following will not work, reading and writing to
		//		ExampleClass2::publicVar instead.
		console.log(this.publicVar);
		this.publicVar = "FIRST CHANGE";
		//	Instead we have to use 'SR'
		console.log(this.SR("ExampleClass1", "publicVar"));
		this.SR("ExampleClass1", "publicVar", "SECOND CHANGE");

		//	Printing the result of the above
		console.log("------------------");
		console.log(this.publicVar);
		console.log(this.SR("ExampleClass2", "publicVar"));
		console.log(this.SR("ExampleClass1", "publicVar"));
	}
});

var instance = crx_new("ExampleClass3");

instance.test();

//SR is also available on the instance
console.log("------------------");
instance.SR("ExampleClass1", "pulicVirtualFunction", 5);
I am ExampleClass3
I am ExampleClass1
I am ExampleClass2
I am ExampleClass2
From ExampleClass2::publicFunction: I am ExampleClass2
From ExampleClass1::publicFunction: I am ExampleClass1
I am ExampleClass3
I am ExampleClass1
------------------
FIRST CHANGE
I am ExampleClass2
SECOND CHANGE
------------------
SECOND CHANGE

3.6.8 ANNUL

ANNUL is a function. For various reasons CrxOop does not give an implementation for destructors. After contemplating many ideas, ANNUL was invented. ANNUL should allow developers to provide their own destructor mechanisms should they need to.

If this.ANNUL() is called, the object pointed to by 'this' is locked. Any further access to the object members will lead to fatal errors. 'ANNUL' must never be passed to functions as parameters or from functions as returns. Misuse of this keyword can also lead to fatal errors.

The following is an example of how a destructor mechanism could be implemented using ANNUL.

(function()
{
	var instances = {};
	var id = 1;

	//	A base class for all our classes that follow the destructable class pattern.
	crx_registerClass('Base',
	{
		PRIVATE:
		{
			VARS:
			{
				'id': 0
			}
		},
		PUBLIC:
		{
			CONSTRUCT: function()
			{
				// 	Saving a reference of the instance into our private
				//		local closure. Why one might need to do this
				//		is beside the point. Assuming you do need to
				//		do this, you need to worry about freeing the
				//		memory when the object is no longer
				//		needed. Look at our 'destruct' function  below.
				this.id = id;
				instances[id] = this.THIS;

				id = id + 1;
			},
			VIRTUAL:
			{
				FUNCTIONS:
				{
					//	Our destructor function.
					'destruct': function()
					{
						//	The following will not delete the instance.
						//		There might be other references	to it
						//		elsewhere.
						delete instances[this.id];
						
						// The following will also not delete the instance.
						//		However, now the instance is roughly as good
						//		as deleted. Any use of this instance later
						//		on will cause fatal errors. This will allow
						//		developers to see these errors before their
						//		code is released.
						this.ANNUL();
					}
				}
			}
		}
	});
})();

crx_registerClass('SomeClass',
{
	EXTENDS: 'Base',
	PRIVATE:
	{
		VARS:
		{
			'someString': null
		}
	},
	PUBLIC:
	{
		FUNCTIONS:
		{
			'setString': function(pString)
			{
				this.someString = pString;
			},
			'doSomething': function()
			{
				console.log(this.someString);
			}
		},
		VIRTUAL:
		{
			FUNCTIONS:
			{
				//	Our destructor function.
				'destruct': function()
				{
					//	Freeing our resource
					this.someString = null;

					//	Allowing upper classes to free their resources.
					//		Notice how we do not call 'ANNUL' here.
					//		This is because 'ANNUL' must be called as the
					//		last line to avoid errors from further access
					//		to the object. This means, the last line in
					//		the last destruct function to be called,
					//		which is Base::destruct()
					this.SR(null, 'destruct');
				}
			}
		}
	}
	
});

var a = crx_new('SomeClass');

a.setString('Some very large resource');
a.doSomething();

//	We are now done.
a.destruct();

//	The following will cause a fatal error.
//a.doSomething()

//	The following will not. 'crxOop.isAnnul' can be used to check if the object has been
//			annuled, which in our case is true.
if(!crxOop.isAnnul(a))
{
	a.doSomething();
}
(function()
{
	var instances = {};
	var id = 1;

	//	A base class for all our classes that follow the destructable class pattern.
	crx_registerClass('Base',
	{
		'VERBOSE': 1,
		'private var id': 0,
		'public CONSTRUCT': function()
		{
			// 	Saving a reference of the instance into our private
			//		local closure. Why one might need to do this
			//		is beside the point. Assuming you do need to
			//		do this, you need to worry about freeing the
			//		memory when the object is no longer
			//		needed. Look at our 'destruct' function  below.
			this.id = id;
			instances[id] = this.THIS;

			id = id + 1;
		},
		//	Our destructor function.
		'public virtual function destruct': function()
		{
			//	The following will not delete the instance.
			//		There might be other references	to it
			//		elsewhere.
			delete instances[this.id];

			// The following will also not delete the instance.
			//		However, now the instance is roughly as good
			//		as deleted. Any use of this instance later
			//		on will cause fatal errors. This will allow
			//		developers to see these errors before their
			//		code is released.
			this.ANNUL();
		}
	});
})();

crx_registerClass('SomeClass',
{
	'VERBOSE': 1,
	'extends': 'Base',
	'private var someString': null
	'public function setString': function(pString)
	{
		this.someString = pString;
	},
	'public function doSomething': function()
	{
		console.log(this.someString);
	},
	//	Our destructor function.
	'public virtual function destruct': function()
	{
		//	Freeing our resource
		this.someString = null;

		//	Allowing upper classes to free their resources.
		//		Notice how we do not call 'ANNUL' here.
		//		This is because 'ANNUL' must be called as the
		//		last line to avoid errors from further access
		//		to the object. This means, the last line in
		//		the last destruct function to be called,
		//		which is Base::destruct
		this.SR(null, 'destruct');
	}
	
});

var a = crx_new('SomeClass');

a.setString('Some very large resource');
a.doSomething();

//	We are now done.
a.destruct();

//	The following will cause a fatal error.
//a.doSomething()

//	The following will not. 'crxOop.isAnnul' can be used
//		to check if the object has been annuled, which
//		in our case is true.
if(!crxOop.isAnnul(a))
{
	a.doSomething();
}

4.0 Interfaces

Initially, in v1.0 of CrxOop, CrxOop did not allow the creation of pure virtual methods, while at the same time the Javscript technology does not allow a satisfactory implementation of multiple inheritance for classes. This leads to the implementation of what are called interfaces in Java. In C++, these would be classes with only public pure virtual methods.

Note the following:

  • Interfaces do not define variables
  • Interfaces do not define full function signatures due to Javascript's limitations, only function names
  • Interface supports multiple inheritance to other interfaces
  • Interface can not be instantiated
  • A class may implement multiple interfaces
  • A class implements an interface by defining a public virtual function, or a public pure virtual function with the same name as the name declared in the interface definition, and doing this for all functions declared in the interface definition.
  • Although virtual functions may have different accessors set going down the class extension chain, once a virtual function is required by an interface, pertaining classes in the class extension chain may only define said virtual function as public.
  • Class instances can not be cast to interfaces
  • Interfaces are still a type, and class instances can be checked against them

4.1 Definition and Registration

Before interfaces can be extended by other classes they have to be defined. During definition write up, you will encounter syntax errors, as with every other code you write in javascript. After fixing the syntax, you will likely encounter Definition Errors, which are CrxOop's equivalent of javascript syntax errors. These must also be fixed before anything happens. Please refer to the section on errors for more information.

After a definition, an interface can either be resgistered explicitly using crx_registerInterface() which would allow you to give it a name, or registered implicitly during calls to other parts of the library, such as crx_new.

Explicit registration is the recommended way of registration, and you can either do it by assigning a definition to a variable and then calling crx_registerInterface(). In this case the interface can be refered to using the variable name of the definition, or the registered interface name.

var InterfaceDefinition =
{
	//DEFINITION
};
crx_registerInterface("myNameSpace.myInterface", InterfaceDefinition);


var ClassDefinition =
{
	IMPLEMENTS: [InterfaceDefinition]
	//REST OF DEFINITION
}
//OR
var ClassDefinition =
{
	IMPLEMENTS: ['myNameSpace.myInterface']
	//REST OF DEFINITION
}

Or by passing the definition immediately to crx_registerInterface(), which is our prefered approach:

crx_registerInterface("myNameSpace.myInterface",
{
	//DEFINITION
});

Note that there is no actual support for name spaces. The full string "myNameSpace.myInterface" is the name of the interface, and not just "myInterface". However the use of ".", or something similar, is useful to avoid name collisions. Also note that interface names can collide with class names and vice versa. Explicit registration is very useful when it comes to definition errors.

The following is an example of implicit registration:

var InterfaceDefinition =
{
	//DEFINITION
};

var ClassDefinition =
{
	IMPLEMENTS: [InterfaceDefinition]
	//REST OF DEFINITION
}
var instance1 = crx_new(ClassDefinition);

Note that interfaces registered implicitly can not be re registered explicitly later on. Also note that interfaces that are not registered implicitly or explicitly do not exist as far as CrxOop is concerned until they are registered. This is important to keep in mind when encountering errors about missing definitions.

4.2 Syntax

Interfaces are defined using plain objects where the keys define the names of functions.

crx_registerInterface("myNameSpace.myInterface",
{
	INHERITS: ["myNameSpace.someOtherInterface1", "myNameSpace.someOtherInterface2"],
	"function1": 0,
	"function2": 0
});

The above interface declares two methods to be implemented by implementing classes along with the methods declared by the two interfaces it inherits, "myNameSpace.someOtherInterface1" and "myNameSpace.someOtherInterface2". An interface may inherit one or more other interfaces, using the definition keyword 'INHERITS'. Interfaces may not inherit other interfaces defining functions with the same name. The same applies for classes, classes may not implement multiple interfaces such as one of the interfaces that it implements defines a method already defined in another interface that it implements.

Classes may implement interfaces using the definition keyword 'INHERITS' as shown below.

Example: Public And Private Functions
crx_registerInterface("ExampleInterface1",
{
	"function1": 0,
	"function2": 0
});
crx_registerInterface("ExampleInterface2",
{
	INHERITS: ["ExampleInterface1"],
	"function3": 0
});
crx_registerInterface("ExampleInterface3",
{
	"function4": 0
});


crx_registerClass("ExampleClass1",
{
	PUBLIC:
	{
		VIRTUAL:
		{
			FUNCTIONS:
			{
				"function1": function(){}
			}
		}
	}
});
crx_registerClass("ExampleClass2",
{
	IMPLEMENTS: ["ExampleInterface2", "ExampleInterface3"],
	EXTENDS: "ExampleClass1",
	PUBLIC:
	{
		VIRTUAL:
		{
			FUNCTIONS:
			{
				"function2": function(){},
				"function3": function(){},
				"function4": function(){}
			}
		}
	}
});
var instance = crx_new("ExampleClass2");
crx_registerInterface("ExampleInterface1",
{
	"function1": 0,
	"function2": 0
});
crx_registerInterface("ExampleInterface2",
{
	INHERITS: ["ExampleInterface1"],
	"function3": 0
});
crx_registerInterface("ExampleInterface3",
{
	"function4": 0
});


crx_registerClass("ExampleClass1",
{
	"VERBOSE": 1,
	"public virtual function function1": function(){}
});
crx_registerClass("ExampleClass2",
{
	"VERBOSE": 1,
	"implements": ["ExampleInterface2", "ExampleInterface3"],
	"extends": "ExampleClass1",
	"public virtual function function2": function(){},
	"public virtual function function3": function(){},
	"public virtual function function4": function(){}
});
var instance = crx_new("ExampleClass2");

Notice how the class "ExampleClass2" did not need to implement "function1" defined in "ExampleInterface1". This is because "function" is already defined by another class in its extension chain. However, CrxOop will issue a warning.

5.0 Structures

5.1 Introduction

Structures are this author's vision, formalation, and generalization of prototypal inheritance and how it would work if javascript was a non scripting for rapid development, non strict, language like C++ or Java. Structure are CrxOop's implementation of Prototype Based Programming, or as this author likes to call it, Prototype Object Based Programming (POBP).

Some history. CrxOop started out as a proof of concept. It was meant to prove that a sufficiently performant implementation of object oriented programming could be added to current javscript engines (browsers) without much difficulty. Later CrxOop grew beyond that to provide full Object Oriented capabilities with Javascript Itself. Classes were meant to provide a facility for developing complex algorithms, and were not suited for data structures due to their large resource consumption. The underlying javascript plain objects were superior for the task, and a simple function to create an object and attach the required fields was always going to be the more efficient solution for data structures.

As it were, Javascript also provides prototypes, and instead one could use constructor functions to fill objects with data and benefit from the added benefits of prototypal inheritance. These benefits, without being stretched, were very limited this author thinks. Stretching, gave rise to different approaches and code recipes to try and mimick concepts from OOP. Code often looked messy. With the strange syntax that prototypes required, and the different code recipes used by developers to mimick things like private variables, code often looked confusing as well. Some developers were resulting to using underscores below variables or functions names to indicate they were private. An unspoken traditions, that is not always possible to tell whether the code is following it or something else.

One of the most important benefits of OOP, this author thinks, is that the code documents itself. With the need for data structures, a new mechanism had to be developed, and since with javascript it is already very easy to built arbitrary objects, this mechanism had to rival the underlying javascript mechanism. It had to reduce to it. In other words, be a super set of it. It had to be efficient. It also had to give the same aformentioned benefits from OOP. This gave rise to Structures.

5.2 Instantiation

The equivalent of the "new" keyword that is found in many programing languages is the function crx_new(). The function works the same way as it does for classes, and has the same exact signature. It is the only way to create structure instances, and provides four overrides which allow the creation of single or multiple instances. All four overrides allow the use of anonymous structure definitions, a feature which can be abused very easily.

crx_new(structureDefinitionOrStructureName, [parameter01, parameter02, ...])
structureDefinitionOrStructureName
Class Definition or String
The structure Definition (object) or name (string)
[parameter01, parameter02, ...]
Mixed
The parameters to be passed to the structure constructor.
Return
Object
A structure instance
Example
crx_registerStructure("MyStructure",
{
	"VERBOSE": 1,
	"public CONSTRUCT": function(pA, pB)
	{
		console.log("(" + pA + "," + pB + ")");
	}
});

var gMyStructure = crx_new("MyStructure", 1, 1);
(1,1)


crx_new(length, structureDefinitionOrStructureName, [parameter01, parameter02, ...])
length
integer
The number of instances to create
structureDefinitionOrStructureName
Class Definition or String
The structure Definition (object) or name (string)
parameter01, parameter02, ...
Mixed
The parameters to be passed to the structure constructor. The parameters are passed for each construction. Optional.
Return
Array
An array of structure instances
Example
crx_registerStructure("MyStructure",
{
	"VERBOSE": 1,
	"public CONSTRUCT": function(pA, pB)
	{
		console.log("(" + pA + "," + pB + ")");
	}
});

var gMyStructure = crx_new(2, "MyStructure", 1, 1);
(1,1)
(1,1)


crx_new(length, parametersArray, structureDefinitionOrStructureName)
parametersArray
Array of Arrays
An an array of arrays such as parametersArray[i] is an array of parameters to be passed to the constructor of instance i; If parametersArray is shorter than the number of instances to create, the last element is used for the construction of the remaining instances.
length
integer
The number of instances to create
structureDefinitionOrStructureName
Class Definition or String
The structure Definition (object) or name (string)
Return
Array
An array of structure instances
Example
crx_registerStructure("MyStructure",
{
	"VERBOSE": 1,
	"public CONSTRUCT": function(pA, pB)
	{
		console.log("(" + pA + "," + pB + ")");
	}
});

var gMyStructure = crx_new(4, [[1,1], [2,2]], "MyStructure");
(1,1)
(2,2)
(2,2)
(2,2)


crx_new(length, parametersFunction, structureDefinitionOrStructureName)
length
integer
The number of instances to create
parametersFunction
function
A function that takes a single integer, which is the current index of the instance created, and returns an array of parameters to be passed to the constructor.
structureDefinitionOrStructureName
Class Definition or String
The structure Definition (object) or name (string)
Return
Array
Returns an array of structure instances
Example
crx_registerStructure("MyStructure",
{
	"VERBOSE": 1,
	"public CONSTRUCT": function(pA, pB)
	{
		console.log("(" + pA + "," + pB + ")");
	}
});

var gMyStructure = crx_new(4, function(pIndex)
{
	return [pIndex, pIndex * pIndex];
}, "MyStructure");
(0,0)
(1,1)
(2,4)
(3,9)

5.3 Definition and Registration

Before structure instances can be made, they must be defined using either of the two syntaxes mentioned in the introduction. Definitions must not be created or altered using calles to defineProperty(), seal() or other similar methods.

During definition write up, you will encounter syntax errors, as with every other code you write in javascript. After fixing the syntax, you will likely encounter Definition Errors, which are CrxOop's equivalent of javascript syntax errors. These must also be fixed before anything happens. Please refer to the section on errors for more information.

After a definition, a structure can either be resgistered explicitly using crx_registerClass() which would allow you to give it a name, or registered implicitly during calls to other parts of the library, such as crx_new().

Explicit registration is the recommended way of registration, and you can either do it by assigning a definition to a variable and then calling crx_registerStructure(). In this case instances can be created using the variable name of the definition, or the registered class name.

var structureDefinition =
{
	//DEFINITION
};
crx_registerStructure("myNameSpace.myStructure", structureDefinition);
var instance1 = crx_new(structureDefinition);
//OR
var instance2 = crx_new("myNameSpace.myStructure");

Or by passing the definition immediately to crx_registerStructure(), which is our prefered approach:

crx_registerStructure("myNameSpace.myStructure",
{
	//DEFINITION
});
var instance2 = crx_new("myNameSpace.myStructure");

Note that there is no actual support for name spaces. The full string "myNameSpace.myStructure" is the name of the structure, and not just "myStructure". However the use of ".", or something similar, is useful to avoid name collisions. Also note that structure names can collide with class names and interface names and vice versa. Explicit registration is very useful when it comes to definition errors.

The following is an example of implicit registration:

var structureDefinition =
{
	//DEFINITION
};
var instance1 = crx_new(structureDefinition);

Note that structures registered implicitly can not be re registered explicitly later on. Also note that structures that are not registered implicitly or explicitly do not exist as far as CrxOop is concerned until they are registered. This is important to keep in mind when encountering errors about missing definitions.

With implicit registration, one can define anonymous structures

var instance1 = crx_new(
{
	//DEFINITION
});

The above can be convenient, however beware. Every such call initiates an internal structures registration, which coupled with the immediate need to build an instance, leads to parsing which means resource consumption. If you need to make more than one instance of a structures, do not declare it anonymously. This does not simply mean do not put the call in a loop. The call could also be in a function called multiple times for example. However, remember that you could always make multiple instances of your anonymous structure using the array form of crx_new, instead of a loop, and because the call to crx_new is only made once this way, you suffer no extra resource loss.

However, unlike with classes, the need for anonymous structure, if used for data structures, should be far less common.

5.3.1 crx_registerStructure()

crx_registerStructure(structureName, structureDefinition)
structureName
String
The structure name (string). It is recommended to use '.' to name space your structure names, but remember CrxOop has no actual support for name spaces. If you call your structure "myNameSpace.myStructure", then the structure is called "myNameSpace.myStructure", and not "myStructure".
structureDefinition
Structure Definition
The structure definition (object)
Example
crx_registerStructure("myNameSpace.myStructure",
{
	//DEFINITION
});

5.4 Structure Components

5.4.1 Constructor

Look at Figure 02. The function CONSTRUCT takes an argument indicating the structure name and returns the contruction function which is an equivilant to constructors in OOP languages. Please note the following

  • The definition keyword CONSTRUCT is case sensitive even in the verbose syntax.
  • A constructor may not throw an exception. This will cause a fatal error.
  • A constructor may call the constructor of the inherited structure, one level up, but no more. This means, for example, that the constructor of a structure may not call the grand parent struture's constructor
  • A default constructor is created when no constructor is defined. The default constructor is a function that takes no arguments.
  • The structure constructor is called automatically with no arguments if the derived/child class constructor did not call it explicitly.
  • A constructor may not return any thing.

To understand how to control construction order, you need to know the steps of construction. Look at the figure below. Given a structure 'D', which inherits class 'C', which itself inherits structures 'A' and 'B', as an example, construction of an instance of D follows the following steps:

  1. The instance is fully created.
  2. The constructor of the last inheriting structure is called first. In our example, this means the constructor of structre 'D'. This is the opposite of C++, for example, and intuition in general.
  3. After the constructor is called, CrxOop checks whether the constructor of the parent structure has been called. If not, the parent structre constructor is called. In our example, the parent constructor would be structure 'C'. If there is more than one immediate parent, CrxOop checks in the order defined in the structure definition (left to right), and calls the yet to be called constructors in that order.
  4. The process repeats until all constructors are called.
  5. The instance is now locked. This protects from new properties being created on the underlying javascript object.

Consider the following code and output:

Instance Construction
crx_registerStructure("StructureA",
{
	"VERBOSE": 1,
	"public CONSTRUCT": function(pA)
	{
		console.log("CONSTRUCTING StructureA using pA = " + pA);
	}
});
crx_registerStructure("StructureB",
{
	"VERBOSE": 1,
	"public CONSTRUCT": function(pA)
	{
		console.log("CONSTRUCTING StructureB using pA = " + pA);
	}
});
crx_registerStructure("StructureC",
{
	"VERBOSE": 1,
	"inherits": ["StructureA", "StructureB"],
	"public CONSTRUCT": function(pA)
	{
		console.log("CONSTRUCTING StructureC using pA = " + pA);
	}
});
crx_registerStructure("StructureD",
{
	"VERBOSE": 1,
	"inherits": ["StructureC"],
	"public CONSTRUCT": function(pA)
	{
		console.log("CONSTRUCTING StructureD using pA = " + pA);
	}
});
crx_new("StructureD", 5);
CONSTRUCTING StructureD using pA = 5
CONSTRUCTING StructureC using pA = undefined
CONSTRUCTING StructureA using pA = undefined
CONSTRUCTING StructureB using pA = undefined

Let us follow what happened:

  • The instance was fully created.
  • The constructor of StructureD was called with the passed in parameter, 5.
  • After the constructor of StructureD finished executing, CrxOop found that the constructor of StructureC was not called and called it without any parameters.
  • After the constructor of StructureC finished executing, CrxOop found that the constructor of StructureA was not called and called it without any parameters.
  • After the constructor of StructureA finished executing, Crxop went back to StructureC, and found that the constructor of StructureB was not called and called it without any parameters.
  • The new instance is now locked.

If we wanted the constructor of StructureB execute its useful code before that of the constructor of StructureC, we call it as the first line of code in the constructor of StructureC. Changing the code of StructureC to the following:

crx_registerStructure("StructureA",
{

.
.
.

crx_registerStructure("StructureC",
{
	"VERBOSE": 1,
	"inherits": ["StructureA", "StructureB"],
	"public CONSTRUCT": function(pA)
	{
		this.CONSTRUCT("StructureB")(pA);
		console.log("CONSTRUCTING StructureC using pA = " + pA);
	}
});
crx_new("ClassC", 5);
CONSTRUCTING StructureD using pA = 5
CONSTRUCTING StructureB using pA = undefined
CONSTRUCTING StructureC using pA = undefined
CONSTRUCTING StructureA using pA = undefined

Let us follow what happened:

  • The instance was fully created.
  • After the constructor of StructureD finished executing, CrxOop found that the constructor of StructureC was not called and called it without any parameters.
  • The constructor of StructureC was called without any parameters, but it called the constructor of StructureB in its first line of code passing the parameter 'undefined', before its own useful code.
  • The constructor of StructureC resumed executing its useful code.
  • After the constructor of StructureC finished executing, CrxOop found that the constructor of StructureA was not called and called it without any parameters.
  • The new instance is now locked.

One very important thing to notice is that the ancestors of StructureC at the StructureB branch fully finished executing their constructors before StructureC finished its own. This is important because it is sufficient in practice. If you are the developer of StructureC, and you want the second ancestor's constructor to be called first, all you care about is the second ancestor of StructureC, StructureB, and its ancestors to finish doing what they need in their constructors before your class begins executing its constructing code. The order of the construction of your ancestors at the StructureB branch would not matter. If it did, it would have been the worry of the developers of StructureB, and its ancesotrs if it had any.

If we want the parameter to be passed up to the constructor of StructureB, we would have to include an explicit constructor call in the constructor of StructureD.

crx_registerStructure("StructureA",
{

.
.
.

crx_registerStructure("StructureD",
{
	"VERBOSE": 1,
	"inherits": ["StructureC"],
	"public CONSTRUCT": function(pA)
	{
		this.CONSTRUCT("StructureC")(pA);
		console.log("CONSTRUCTING StructureD using pA = " + pA);
	}
});
crx_new("ClassC", 5);
CONSTRUCTING StructureB using pA = 5
CONSTRUCTING StructureC using pA = 5
CONSTRUCTING StructureA using pA = undefined
CONSTRUCTING StructureD using pA = 5

5.4.2 Shared Public Instance Variables and Functions

Shared public structure members are equivilant to public class members in their access. However, unlike in classes where each class in the extension chain has its own copy of its non virtual members in an instance, structure instances contain one copy of each shared public member. The one last decalred in the inheritance chain. Shared public structure members can be thought of as public 'virtual' class members, both functions and variables.

Example: Shared Public Access Members
crx_registerStructure("ExampleStructureA",
{
	SHARED:
	{
		PUBLIC:
		{
			VARS:
			{
				"sharedPublicVar": "I am ExampleStructureA::sharedPublicVar",
			},
			FUNCTIONS:
			{
				"sharedPublicFunction": function()
				{
					return "I am ExampleStructureA::sharedPublicFunction()";
				},
				"testFromExampleStructureA": function()
				{
					console.log(this.sharedPublicVar);
					console.log(this.sharedPublicFunction());
				}
			}
		}
	}
});
crx_registerStructure("ExampleStructureB",
{
	INHERITS: ["ExampleStructureA"],
	SHARED:
	{
		PUBLIC:
		{
			VARS:
			{
				"sharedPublicVar": "I am ExampleStructureB::sharedPublicVar",
			},
			FUNCTIONS:
			{
				"sharedPublicFunction": function()
				{
					return "I am ExampleStructureB::sharedPublicFunction()";
				},
				"testFromExampleStructureB": function()
				{
					console.log(this.sharedPublicVar);
					console.log(this.sharedPublicFunction());
				}
			}
		}
	}
});

var a = crx_new("ExampleStructureB");

console.log("From out side the instance functions");
console.log(a.sharedPublicVar);
console.log(a.sharedPublicFunction());
console.log("From inside ExampleStructureA function in an ExampleStructureB instance");
a.testFromExampleStructureA();
console.log("From inside ExampleStructureB function in an ExampleStructureB instance");
a.testFromExampleStructureB();
From out side the instance functions
I am ExampleStructureB::sharedPublicVar
I am ExampleStructureB::sharedPublicFunction()
From inside ExampleStructureA function in an ExampleStructureB instance
I am ExampleStructureB::sharedPublicVar
I am ExampleStructureB::sharedPublicFunction()
From inside ExampleStructureB function in an ExampleStructureB instance
I am ExampleStructureB::sharedPublicVar
I am ExampleStructureB::sharedPublicFunction()
crx_registerStructure("ExampleStructureA",
{
	VERBOSE: 1,
	"shared public var sharedPublicVar": "I am ExampleStructureA::sharedPublicVar",
	"shared public function sharedPublicFunction": function()
	{
		return "I am ExampleStructureA::sharedPublicFunction()";
	},
	"shared public function testFromExampleStructureA": function()
	{
		console.log(this.sharedPublicVar);
		console.log(this.sharedPublicFunction());
	}
});
crx_registerStructure("ExampleStructureB",
{
	VERBOSE: 1,
	INHERITS: ["ExampleStructureA"],
	"shared public var sharedPublicVar": "I am ExampleStructureB::sharedPublicVar",
	"shared public function sharedPublicFunction": function()
	{
		return "I am ExampleStructureB::sharedPublicFunction()";
	},
	"shared public function testFromExampleStructureB": function()
	{
		console.log(this.sharedPublicVar);
		console.log(this.sharedPublicFunction());
	}
});

var a = crx_new("ExampleStructureB");

console.log("From out side the instance functions");
console.log(a.sharedPublicVar);
console.log(a.sharedPublicFunction());
console.log("From inside ExampleStructureA function in an ExampleStructureB instance");
a.testFromExampleStructureA();
console.log("From inside ExampleStructureB function in an ExampleStructureB instance");
a.testFromExampleStructureB();
From out side the instance functions
I am ExampleStructureB::sharedPublicVar
I am ExampleStructureB::sharedPublicFunction()
From inside ExampleStructureA function in an ExampleStructureB instance
I am ExampleStructureB::sharedPublicVar
I am ExampleStructureB::sharedPublicFunction()
From inside ExampleStructureB function in an ExampleStructureB instance
I am ExampleStructureB::sharedPublicVar
I am ExampleStructureB::sharedPublicFunction()

5.4.3 Shared Private Instance Variables and Functions

Shared private structure members can be accessed from any structure function within the instance, regardless of the structure. Further more, like the shared public members, structure instances contain only one copy of each shared private member. The one last decalred in the inheritance chain. Shared private structure members can be thought of as protected 'virtual' class members, both functions and variables, roughly. It is recommended that you refer to the section about 'O'.

Example: Shared Public Access Members
crx_registerStructure("ExampleStructureA",
{
	SHARED:
	{
		PRIVATE:
		{
			VARS:
			{
				"sharedPrivateVar": "I am ExampleStructureA::sharedPrivateVar",
				"sharedPrivateVar2": "I am ExampleStructureA::sharedPrivateVar2",
			},
			FUNCTIONS:
			{
				"sharedPrivateFunction": function()
				{
					return "I am ExampleStructureA::sharedPrivateFunction()";
				}
			}
		},
		PUBLIC:
		{
			FUNCTIONS:
			{
				"testFromExampleStructureA": function()
				{
					console.log(this.sharedPrivateVar);
					console.log(this.sharedPrivateFunction());
				}
			}
		}
	}
});
crx_registerStructure("ExampleStructureB",
{
	INHERITS: ["ExampleStructureA"],
	SHARED:
	{
		PRIVATE:
		{
			VARS:
			{
				"sharedPrivateVar": "I am ExampleStructureB::sharedPrivateVar",
			},
			FUNCTIONS:
			{
				"sharedPrivateFunction": function()
				{
					return "I am ExampleStructureB::sharedPrivateFunction()";
				}
			}
		},
		PUBLIC:
		{
			FUNCTIONS:
			{
				"testFromExampleStructureB": function()
				{
					console.log(this.sharedPrivateVar);
					console.log(this.sharedPrivateFunction());

					//	Notice how we can still access the shared 'private' variable
					//		that is defined in ExampleStructureA only. This applies
					//		to shared private functions too.
					console.log(this.sharedPrivateVar2);
				}
			}
		}
	}
});

var a = crx_new("ExampleStructureB");

console.log("From out side the instance functions");
console.log(a.sharedPrivateVar);
console.log(a.sharedPrivateFunction);
console.log("From inside ExampleStructureA function in an ExampleStructureB instance");
a.testFromExampleStructureA();
console.log("From inside ExampleStructureB function in an ExampleStructureB instance");
a.testFromExampleStructureB();
From out side the instance functions
undefined
undefined
From inside ExampleStructureA function in an ExampleStructureB instance
I am ExampleStructureB::sharedPrivateVar
I am ExampleStructureB::sharedPrivateFunction()
From inside ExampleStructureB function in an ExampleStructureB instance
I am ExampleStructureB::sharedPrivateVar
I am ExampleStructureB::sharedPrivateFunction()
I am ExampleStructureA::sharedPrivateVar2
crx_registerStructure("ExampleStructureA",
{
	VERBOSE: 1,
	"shared private var sharedPrivateVar": "I am ExampleStructureA::sharedPrivateVar",
	"shared private var sharedPrivateVar2": "I am ExampleStructureA::sharedPrivateVar2",
	"shared private function sharedPrivateFunction": function()
	{
		return "I am ExampleStructureA::sharedPrivateFunction()";
	},
	"shared public function testFromExampleStructureA": function()
	{
		console.log(this.sharedPrivateVar);
		console.log(this.sharedPrivateFunction());
	}
});
crx_registerStructure("ExampleStructureB",
{
	VERBOSE: 1,
	INHERITS: ["ExampleStructureA"],
	"shared private var sharedPrivateVar": "I am ExampleStructureB::sharedPrivateVar",
	"shared private function sharedPrivateFunction": function()
	{
		return "I am ExampleStructureB::sharedPrivateFunction()";
	},
	"shared public function testFromExampleStructureB": function()
	{
		console.log(this.sharedPrivateVar);
		console.log(this.sharedPrivateFunction());

		//	Notice how we can still access the shared 'private' variable
		//		that is defined in ExampleStructureA only. This applies
		//		to shared private functions too.
		console.log(this.sharedPrivateVar2);
	}
});

var a = crx_new("ExampleStructureB");

console.log("From out side the instance functions");
console.log(a.sharedPrivateVar);
console.log(a.sharedPrivateFunction);
console.log("From inside ExampleStructureA function in an ExampleStructureB instance");
a.testFromExampleStructureA();
console.log("From inside ExampleStructureB function in an ExampleStructureB instance");
a.testFromExampleStructureB();
From out side the instance functions
undefined
undefined
From inside ExampleStructureA function in an ExampleStructureB instance
I am ExampleStructureB::sharedPrivateVar
I am ExampleStructureB::sharedPrivateFunction()
From inside ExampleStructureB function in an ExampleStructureB instance
I am ExampleStructureB::sharedPrivateVar
I am ExampleStructureB::sharedPrivateFunction()
I am ExampleStructureA::sharedPrivateVar2

5.4.4 Private Instance Variables and Functions

Private structure members are exactly equivilant to private class members.

Example: Private Access Members
crx_registerStructure("ExampleStructureA",
{
	PRIVATE:
	{
		VARS:
		{
			"privateVar": "I am ExampleStructureA::privateVar",
			"privateVar2": "I am ExampleStructureA::privateVar2"
		},
		FUNCTIONS:
		{
			"privateFunction": function()
			{
				return "I am ExampleStructureA::privateFunction()";
			}
		}
	},
	SHARED:
	{
		PUBLIC:
		{
			FUNCTIONS:
			{
				"testFromExampleStructureA": function()
				{
					console.log(this.privateVar);
					console.log(this.privateFunction());
					console.log(this.privateVar2);
				}
			}
		}
	}
});
crx_registerStructure("ExampleStructureB",
{
	INHERITS: ["ExampleStructureA"],
	PRIVATE:
	{
		VARS:
		{
			"privateVar": "I am ExampleStructureB::privateVar",
		},
		FUNCTIONS:
		{
			"privateFunction": function()
			{
				return "I am ExampleStructureB::privateFunction()";
			}
		}
	},
	SHARED:
	{
		PUBLIC:
		{
			FUNCTIONS:
			{
				"testFromExampleStructureB": function()
				{
					console.log(this.privateVar);
					console.log(this.privateFunction());
					console.log(this.privateVar2);
				}
			}
		}
	}
});

var a = crx_new("ExampleStructureB");

console.log("From out side the instance functions");
console.log(a.privateVar);
console.log(a.privateFunction);
console.log("From inside ExampleStructureA function in an ExampleStructureB instance");
a.testFromExampleStructureA();
console.log("From inside ExampleStructureB function in an ExampleStructureB instance");
a.testFromExampleStructureB();
From out side the instance functions
undefined
undefined
From inside ExampleStructureA function in an ExampleStructureB instance
I am ExampleStructureA::privateVar
I am ExampleStructureA::privateFunction()
I am ExampleStructureA::privateVar2
From inside ExampleStructureB function in an ExampleStructureB instance
I am ExampleStructureB::privateVar
I am ExampleStructureB::privateFunction()
undefined
crx_registerStructure("ExampleStructureA",
{
	VERBOSE: 1,
	"private var privateVar": "I am ExampleStructureA::privateVar",
	"private var privateVar2": "I am ExampleStructureA::privateVar2",
	"private function privateFunction": function()
	{
		return "I am ExampleStructureA::privateFunction()";
	},
	"shared public function testFromExampleStructureA": function()
	{
		console.log(this.privateVar);
		console.log(this.privateFunction());
		console.log(this.privateVar2);
	}
});
crx_registerStructure("ExampleStructureB",
{
	VERBOSE: 1,
	INHERITS: ["ExampleStructureA"],
	"private var privateVar": "I am ExampleStructureB::privateVar",
	"private function privateFunction": function()
	{
		return "I am ExampleStructureB::privateFunction()";
	},
	"shared public function testFromExampleStructureB": function()
	{
		console.log(this.privateVar);
		console.log(this.privateFunction());
		console.log(this.privateVar2);
	}
});

var a = crx_new("ExampleStructureB");

console.log("From out side the instance functions");
console.log(a.privateVar);
console.log(a.privateFunction);
console.log("From inside ExampleStructureA function in an ExampleStructureB instance");
a.testFromExampleStructureA();
console.log("From inside ExampleStructureB function in an ExampleStructureB instance");
a.testFromExampleStructureB();
From out side the instance functions
undefined
undefined
From inside ExampleStructureA function in an ExampleStructureB instance
I am ExampleStructureA::privateVar
I am ExampleStructureA::privateFunction()
I am ExampleStructureA::privateVar2
From inside ExampleStructureB function in an ExampleStructureB instance
I am ExampleStructureB::privateVar
I am ExampleStructureB::privateFunction()
undefined

Private variables are the most expensive memory wise, but perhaps they are still cheaper overall than making use of var and closures the way traditionaly this is done. Further more, this privacy is not bound to the instance itself only, which is what you would expect from private access. A behavior that can not be mimicked using the traditional techniques only. Refer to 'O'.

5.5 Structure Keywords

5.5.1 this, THIS

'this' and 'THIS' are perhaps the most important of the structure keywords to understand. Internally, a structure instance consists of a shared public memory sector, a shared private memory sector, and a private memory sector. The 'this' keyword, which is the native Javascript 'this' keyword has access to all three sectors. 'THIS' on the other hand has access only to the shared public memory sector. This is why passing 'this' to functions as parameters, or from functions as returns, is dangerous and must never be done. Instead you would pass around 'THIS'.

crx_registerStructure("ExampleStructure",
{
	SHARED:
	{
		PUBLIC:
		{
			VARS:
			{
				"sharedPublicVar": 1
			},
			FUNCTIONS:
			{
				"sharedPublicFunction": function(pExampleStructure)
				{
					//	Remember 'THIS' is a structure keyword and hence is found on
					//		'this' and can be accessed using either 'this':
					console.log(this.THIS.sharedPublicVar);
					//		although for clarity you should never use 'THIS' to
					//		access instance members but instead use
					console.log(this.sharedPublicVar);

					//	Or the object that is the structure instance:
					console.log(pExampleStructure.THIS.sharedPublicVar);
					//		but again you should never use 'THIS' to access
					//		instance members but instead use
					console.log(pExampleStructure.sharedPublicVar);

					//	'THIS' is only meant for passing around. When wanting to
					//		return the instance from functions, instead of the
					//		dangerous code "return this", you would use
					return this.THIS;
				},
				"sharedPublicFunction2": function()
				{
					// 	And when wanting to pass the instance to functions,
					//		instead of passing 'this', you would pass
					// 		"this.THIS" like the following
					var vReturn = this.sharedPublicFunction(this.THIS);


					//	Imagine the danger of the following if sharedPublicFunction
					//		above returned 'this' instead of 'THIS'
					return vReturn;
				}
			}
		}
	}
});
crx_registerStructure("ExampleStructure",
{
	"VERBOSE": 1,
	"shared public var sharedPublicVar": 1,
	"shared public function sharedPublicFunction": function(pExampleStructure)
	{
		//	Remember 'THIS' is a structure keyword and hence is found on
		//		'this' and can be accessed using either 'this':
		console.log(this.THIS.sharedPublicVar);
		//		although for clarity you should never use 'THIS' to
		//		access instance members but instead use
		console.log(this.sharedPublicVar);

		//	Or the object that is the structure instance:
		console.log(pExampleStructure.THIS.sharedPublicVar);
		//		but again you should never use 'THIS' to access
		//		instance members but instead use
		console.log(pExampleStructure.sharedPublicVar);

		//	'THIS' is only meant for passing around. When wanting to
		//		return the instance from functions, instead of the
		//		dangerous code "return this", you would use
		return this.THIS;
	},
	"shared public function sharedPublicFunction2": function()
	{
		// 	And when wanting to pass the instance to functions,
		//		instead of passing 'this', you would pass
		// 		"this.THIS" like the following
		var vReturn = this.sharedPublicFunction(this.THIS);


		//	Imagine the danger of the following if sharedPublicFunction
		//		above returned 'this' instead of 'THIS'
		return vReturn;
	}
});

5.5.2 O

'O' is a function. While 'this' allows you access to private variables and functions, as well as shared privates and shared publics, of the current instance, 'O' allows access to privates and shared privates of other instances. Internally 'O' will automatically return 'this', not 'THIS', of the object. Hence 'O' must never be passed to functions as parameters or from functions as returns, and because 'O' returns 'this', the rule applies to its return too. 'O' returns null if an error.

crx_registerStructure("ExampleStructure1",
{
	SHARED:
	{
		PUBLIC:
		{
			VARS:
			{
				"sharedPublicVar": "I am the structure's sharedPublicVar"
			},
			FUNCTIONS:
			{
				"sharedPublicFunction": function(pExampleStructure1)
				{
					//	Accessing private members
					console.log(this.O(pExampleStructure1).privateVar);

					//	And shared private members
					console.log(this.O(pExampleStructure1).sharedPrivateVar);

					//	And also shared publics,
					console.log(this.O(pExampleStructure1).sharedPublicVar);
					//	but never do this, instead do,
					console.log(pExampleStructure1.sharedPublicVar);


					//	Like 'this' in the previous section, 'O' must never be
					//		passed around, but this also applies to the return
					//		of 'O' which is another 'this'. For example, instead
					//		of returning this.O(pExampleStructure1) to return the
					//		instance pExampleStructure1, do the following
					return this.O(pExampleStructure1).THIS;
				}
			}
		},
		PRIVATE:
		{
			VARS:
			{
				"sharedPrivateVar": "I am the structure's sharedPrivateVar"
			}
		}
	},
	PRIVATE:
	{
		VARS:
		{
			"privateVar": "ExampleStructure1::privateVar"
		}
	}
});
crx_registerStructure("ExampleStructure2",
{
	INHERITS: ["ExampleStructure1"],
	SHARED:
	{
		PUBLIC:
		{
			VARS:
			{
				"sharedPublicVar": "I am the structure's sharedPublicVar (2)"
			},
			FUNCTIONS:
			{
				"test": function(pExampleStructre2)
				{
					this.sharedPublicFunction(pExampleStructre2);
				}
			}
		}
	}
});

var instance = crx_new("ExampleStructure2");

instance.test(crx_new("ExampleStructure2"));
ExampleStructure1::privateVar
I am the structure's sharedPrivateVar
I am the structure's sharedPublicVar (2)
I am the structure's sharedPublicVar (2)
crx_registerStructure("ExampleStructure1",
{
	"VERBOSE": 1,
	"shared public var sharedPublicVar": "I am the structure's sharedPublicVar",
	"shared public function sharedPublicFunction": function(pExampleStructure1)
	{
		//	Accessing private members
		console.log(this.O(pExampleStructure1).privateVar);

		//	And shared private members
		console.log(this.O(pExampleStructure1).sharedPrivateVar);

		//	And also publics,
		console.log(this.O(pExampleStructure1).sharedPublicVar);
		//	but never do this, instead do,
		console.log(pExampleStructure1.sharedPublicVar);


		//	Like 'this' in the previous section, 'O' must never be
		//		passed around, but this also applies to the return
		//		of 'O' which is another 'this'. For example, instead
		//		of returning this.O(pExampleStructure1) to return the
		//		instance pExampleStructure1, do the following
		return this.O(pExampleStructure1).THIS;
	},
	"shared private var sharedPrivateVar": "I am the structure's sharedPrivateVar",
	"private var privateVar": "ExampleStructure1::privateVar"
});
crx_registerStructure("ExampleStructure2",
{
	"VERBOSE": 1,
	"inherits": ["ExampleStructure1"],
	"shared public var sharedPublicVar": "I am the structure's sharedPublicVar (2)",
	"shared public function test": function(pExampleStructre2)
	{
		this.sharedPublicFunction(pExampleStructre2);
	}
});

var instance = crx_new("ExampleStructure2");

instance.test(crx_new("ExampleStructure2"));
ExampleStructure1::privateVar
I am the structure's sharedPrivateVar
I am the structure's sharedPublicVar (2)
I am the structure's sharedPublicVar (2)

5.5.3 CONSTRUCT

'CONSTRUCT' is a function. The structure keyword 'CONSTRUCT' is not to be confused with the definition keyword 'CONSTRUCT'. 'CONSTRUCT' returns the constructor function defined in the class definition, and is only available during construction. The only valid use of CONSTRUCT is to call a parent constructor using "this.CONSTRUCT('nameOfInheritedStructure')()" when an explicit call is needed. Never pass "CONSTRUCT" to functions as parameters, or from functions as returns.

crx_registerStructure("ExampleStructure1",
{
	PUBLIC:
	{
		CONSTRUCT: function(pValue)
		{
			if(pValue !== undefined)
				{this.privateVar = pValue;}
		},
	},
	SHARED:
	{
		PUBLIC:
		{
			FUNCTIONS:
			{
				'test': function()
				{
					console.log(this.privateVar);
				}
			}
		}
	},
	PRIVATE:
	{
		VARS:
		{
			"privateVar": "I am default privateVar"
		}
	}
});

crx_registerStructure("ExampleStructure2",
{
	PUBLIC:
	{
		CONSTRUCT: function(pValue)
			{}
	}
});

crx_registerStructure("ExampleStructure3",
{
	INHERITS: ["ExampleStructure1"],
	PUBLIC:
	{
		CONSTRUCT: function(pValue)
		{
			//	Calling a parent structure's constructor explicitly to
			//		pass the necessary parameters
			this.CONSTRUCT("ExampleStructure1")(pValue);
		}
	}
});

crx_registerStructure("ExampleClass4",
{
	INHERITS: ["ExampleStructure2", "ExampleStructure3"],
	PUBLIC:
	{
		CONSTRUCT: function(pValue)
		{
			//	Calling a parent structure's constructor explicitly to
			//		pass the necessary parameters
			this.CONSTRUCT("ExampleStructure3")(pValue);
		}
	}
});

var instance = crx_new("ExampleClass4", "I am a new privateVar");

instance.test();
I am a new privateVar
crx_registerStructure("ExampleStructure1",
{
	"VERBOSE": 1,
	"public CONSTRUCT": function(pValue)
	{
		if(pValue !== undefined)
			{this.privateVar = pValue;}
	},
	"shared public function test": function()
	{
		console.log(this.privateVar);
	},
	"private var privateVar": "I am default privateVar"
});

crx_registerStructure("ExampleStructure2",
{
	"VERBOSE": 1,
	"public CONSTRUCT": function(pValue)
		{}
});

crx_registerStructure("ExampleStructure3",
{
	"VERBOSE": 1,
	"inherits": ["ExampleStructure1"],
	"public CONSTRUCT": function(pValue)
	{
		//	Calling the parent class's constructor explicitly to
		//		pass the necessary parameters
		this.CONSTRUCT("ExampleStructure1")(pValue);
	}
});

crx_registerStructure("ExampleStructure4",
{
	"VERBOSE": 1,
	"inherits": ["ExampleStructure2", "ExampleStructure3"],
	"public CONSTRUCT": function(pValue)
	{
		//	Calling a parent structure's constructor explicitly to
		//		pass the necessary parameters
		this.CONSTRUCT("ExampleStructure3")(pValue);
	}
});

var instance = crx_new("ExampleStructure4", "I am a new privateVar");

instance.test();
I am a new privateVar

5.5.4 SR

"SR" is roughly the equivilant of "::" in C++. However, it can only be used on instances. Its useful to accessing functions that are defined as shared public, or shared private, but were overriden by inheriting structures. The use of "SR" should generally be kept to a minimum.

"SR" can not be used to access variables. This is unlike SR for classes. "SR" does not allow the use of null for the structure name, also unlike SR for classes.

"SR", like "O" for example, must never be passed to functions as parameters, or returned from functions as returns. "SR" will cause fatal errors if the structure is not in the instance's structure inheritance map at all, or if misused.

crx_registerStructure("ExampleStructure1",
{
	SHARED:
	{
		PUBLIC:
		{
			FUNCTIONS:
			{
				sharedPulicFunction: function(pA)
				{
					return ("ExampleStructure1::sharedPulicFunction with parameter " + pA);
				},
				testFromExampleStructure1: function(pA)
				{
					//	Calling ExampleStructure1::sharedPulicFunction or
					//		ExampleStructure1::sharedPrivateFunction
					//		will not work if an inheriting structure in the instance
					//		overrides the functions, like ExampleStructure3.
					this.sharedPulicFunction(5);
					this.sharedPrivateFunction(6);
					//	Instead we have to use 'SR'
					this.SR("ExampleStructure1", "sharedPulicFunction", 5);
					this.SR("ExampleStructure1", "sharedPrivateFunction", 6);

					//	The following would call
					//		ExampleStructure2::sharedPulicFunction
					this.SR("ExampleStructure2", "sharedPulicFunction", 7);

					//	Printing the result of the above
					console.log("TESTING FROM ExampleStructure1");
					console.log(this.sharedPulicFunction(5));
					console.log(this.sharedPrivateFunction(6));
					console.log(this.SR("ExampleStructure1", "sharedPulicFunction", 5));
					console.log(this.SR("ExampleStructure1", "sharedPrivateFunction", 6));
					console.log(this.SR("ExampleStructure2", "sharedPulicFunction", 7));
				}
			}
		},
		PRIVATE:
		{
			FUNCTIONS:
			{
				sharedPrivateFunction: function(pA)
				{
					return ("ExampleStructure1::sharedPrivateFunction with parameter " + pA);
				}
			}
		}
	}
});
crx_registerStructure("ExampleStructure2",
{
	SHARED:
	{
		PUBLIC:
		{
			FUNCTIONS:
			{
				sharedPulicFunction: function(pA)
				{
					return ("ExampleStructure2::sharedPulicFunction with parameter " + pA);
				}
			}
		},
		PRIVATE:
		{
			FUNCTIONS:
			{
				sharedPrivateFunction: function(pA)
				{
					return ("ExampleStructure2::sharedPrivateFunction with parameter " + pA);
				}
			}
		}
	}
});
crx_registerStructure("ExampleStructure3",
{
	INHERITS: ["ExampleStructure1", "ExampleStructure2"],
	SHARED:
	{
		PUBLIC:
		{
			FUNCTIONS:
			{
				sharedPulicFunction: function(pA)
				{
					return ("ExampleStructure3::sharedPulicFunction with parameter " + pA);
				},
				testFromExampleStructure3: function(pA)
				{
					this.sharedPulicFunction(8);
					this.sharedPrivateFunction(9);

					this.SR("ExampleStructure1", "sharedPulicFunction", 8);
					this.SR("ExampleStructure1", "sharedPrivateFunction", 9);

					this.SR("ExampleStructure2", "sharedPulicFunction", 8);
					this.SR("ExampleStructure2", "sharedPrivateFunction", 9);

					//	Printing the result of the above
					console.log("TESTING FROM ExampleStructure3");
					console.log(this.sharedPulicFunction(8));
					console.log(this.sharedPrivateFunction(9));
					console.log(this.SR("ExampleStructure1", "sharedPulicFunction", 8));
					console.log(this.SR("ExampleStructure1", "sharedPrivateFunction", 9));
					console.log(this.SR("ExampleStructure2", "sharedPulicFunction", 8));
					console.log(this.SR("ExampleStructure2", "sharedPrivateFunction", 9));
				}
			}
		},
		PRIVATE:
		{
			FUNCTIONS:
			{
				sharedPrivateFunction: function(pA)
				{
					return ("ExampleStructure3::sharedPrivateFunction with parameter " + pA);
				}
			}
		}
	}
});

var instance = crx_new("ExampleStructure3");

instance.testFromExampleStructure1();
instance.testFromExampleStructure3();

//SR is also available on the instance
console.log("------------------");
console.log(instance.SR("ExampleStructure2", "sharedPulicFunction", 10));
TESTING FROM ExampleStructure1
ExampleStructure3::sharedPulicFunction with parameter 5
ExampleStructure3::sharedPrivateFunction with parameter 6
ExampleStructure1::sharedPulicFunction with parameter 5
ExampleStructure1::sharedPrivateFunction with parameter 6
ExampleStructure2::sharedPulicFunction with parameter 7
TESTING FROM ExampleStructure3
ExampleStructure3::sharedPulicFunction with parameter 8
ExampleStructure3::sharedPrivateFunction with parameter 9
ExampleStructure1::sharedPulicFunction with parameter 8
ExampleStructure1::sharedPrivateFunction with parameter 9
ExampleStructure2::sharedPulicFunction with parameter 8
ExampleStructure2::sharedPrivateFunction with parameter 9
------------------
ExampleStructure2::sharedPulicFunction with parameter 10
crx_registerStructure("ExampleStructure1",
{
	VERBOSE: 1,
	"shared public function sharedPulicFunction": function(pA)
	{
		return ("ExampleStructure1::sharedPulicFunction with parameter " + pA);
	},
	"shared public function testFromExampleStructure1": function(pA)
	{
		//	Calling ExampleStructure1::sharedPulicFunction or
		//		ExampleStructure1::sharedPrivateFunction
		//		will not work if an inheriting structure in the instance
		//		overrides the functions, like ExampleStructure3.
		this.sharedPulicFunction(5);
		this.sharedPrivateFunction(6);
		//	Instead we have to use 'SR'
		this.SR("ExampleStructure1", "sharedPulicFunction", 5);
		this.SR("ExampleStructure1", "sharedPrivateFunction", 6);

		//	The following would call
		//		ExampleStructure2::sharedPulicFunction
		this.SR("ExampleStructure2", "sharedPulicFunction", 7);

		//	Printing the result of the above
		console.log("TESTING FROM ExampleStructure1");
		console.log(this.sharedPulicFunction(5));
		console.log(this.sharedPrivateFunction(6));
		console.log(this.SR("ExampleStructure1", "sharedPulicFunction", 5));
		console.log(this.SR("ExampleStructure1", "sharedPrivateFunction", 6));
		console.log(this.SR("ExampleStructure2", "sharedPulicFunction", 7));
	},
	"shared private function sharedPrivateFunction": function(pA)
	{
		return ("ExampleStructure1::sharedPrivateFunction with parameter " + pA);
	}
});
crx_registerStructure("ExampleStructure2",
{
	VERBOSE: 1,
	"shared public function sharedPulicFunction": function(pA)
	{
		return ("ExampleStructure2::sharedPulicFunction with parameter " + pA);
	},
	"shared private function sharedPrivateFunction": function(pA)
	{
		return ("ExampleStructure2::sharedPrivateFunction with parameter " + pA);
	}
});
crx_registerStructure("ExampleStructure3",
{
	VERBOSE: 1,
	"inherits": ["ExampleStructure1", "ExampleStructure2"],
	"shared public function sharedPulicFunction": function(pA)
	{
		return ("ExampleStructure3::sharedPulicFunction with parameter " + pA);
	},
	"shared public function testFromExampleStructure3": function(pA)
	{
		this.sharedPulicFunction(8);
		this.sharedPrivateFunction(9);

		this.SR("ExampleStructure1", "sharedPulicFunction", 8);
		this.SR("ExampleStructure1", "sharedPrivateFunction", 9);

		this.SR("ExampleStructure2", "sharedPulicFunction", 8);
		this.SR("ExampleStructure2", "sharedPrivateFunction", 9);

		//	Printing the result of the above
		console.log("TESTING FROM ExampleStructure3");
		console.log(this.sharedPulicFunction(8));
		console.log(this.sharedPrivateFunction(9));
		console.log(this.SR("ExampleStructure1", "sharedPulicFunction", 8));
		console.log(this.SR("ExampleStructure1", "sharedPrivateFunction", 9));
		console.log(this.SR("ExampleStructure2", "sharedPulicFunction", 8));
		console.log(this.SR("ExampleStructure2", "sharedPrivateFunction", 9));
	},
	"shared private function sharedPrivateFunction": function(pA)
	{
		return ("ExampleStructure3::sharedPrivateFunction with parameter " + pA);
	}
});

var instance = crx_new("ExampleStructure3");

instance.testFromExampleStructure1();
instance.testFromExampleStructure3();

//SR is also available on the instance
console.log("------------------");
console.log(instance.SR("ExampleStructure2", "sharedPulicFunction", 10));
TESTING FROM ExampleStructure1
ExampleStructure3::sharedPulicFunction with parameter 5
ExampleStructure3::sharedPrivateFunction with parameter 6
ExampleStructure1::sharedPulicFunction with parameter 5
ExampleStructure1::sharedPrivateFunction with parameter 6
ExampleStructure2::sharedPulicFunction with parameter 7
TESTING FROM ExampleStructure3
ExampleStructure3::sharedPulicFunction with parameter 8
ExampleStructure3::sharedPrivateFunction with parameter 9
ExampleStructure1::sharedPulicFunction with parameter 8
ExampleStructure1::sharedPrivateFunction with parameter 9
ExampleStructure2::sharedPulicFunction with parameter 8
ExampleStructure2::sharedPrivateFunction with parameter 9
------------------
ExampleStructure2::sharedPulicFunction with parameter 10

5.5.5 HASOWN

"HASOWN" is the equivilant of the built in hasOwnProperty function and is meant to be used for iteration on the structure members.

HASOWN(memberName, scopeFlags, typeFlags, isExclusive)
memberName
String
The member name.
scopeFlags
integer
The flag filters the members iterated over based on their access level. These are:

crxOop.HASOWN_SCOPE_SHARED_PUBLIC = 8 for members that are "shared public"
crxOop.HASOWN_SCOPE_SHARED_PRIVATE = 16 for members that are "shared private"
crxOop.HASOWN_SCOPE_PRIVATE = 32 for members that are "private"

Defaults to 8 (crxOop.HASOWN_SCOPE_SHARED_PUBLIC)
scopeFlags
integer
The flag filters the members based on their type. These are:

crxOop.HASOWN_TYPE_VAR = 1 for members that are variables
crxOop.HASOWN_TYPE_FUNCTION = 2 for members that are functions
crxOop.HASOWN_TYPE_FOREIGN = 4 for members that were not defined in the structure definition

Defaults to 1 (crxOop.HASOWN_TYPE_VAR)

Foreign members are members declared in the constructor, not in the class definition. Such declaration is highly discouraged, but allowed.
isExclusive
boolean
If true, selects only members belonging to the structure itself, which include those defined by the structure and those defined by the structure's ancestors. Otherwise, selects all memebers visible to the structure. Note that foreign members would not be selected if set to true.
Return
Object
true or false
Example
crx_registerStructure("MyStructure1",
{
	"VERBOSE": 1,
	"shared public var sharedPublicVar": "MyStructure1::sharedPublicVar",
	"shared public function sharedPublicFunction": function()
	{
		return "MyStructure1::sharedPublicFunction()";
	},
	"shared private var sharedPrivateVar": "MyStructure1::sharedPrivateVar",
	"shared private function sharedPrivateFunction": function()
	{
		return "MyStructure1::sharedPrivateFunction()";
	},
	"private var privateVar": "MyStructure1::privateVar",
	"shared public function test2": function()
	{
		console.log("---");
		console.log("---");
		console.log("Iterating over all variables and functions of current structure (MyStructure1)");
		console.log("---");
		
		for(tKey in this)
		{
			if(this.HASOWN(tKey, crxOop.HASOWN_SCOPE_SHARED_PUBLIC | crxOop.HASOWN_SCOPE_SHARED_PRIVATE |
					crxOop.HASOWN_SCOPE_PRIVATE, crxOop.HASOWN_TYPE_VAR | crxOop.HASOWN_TYPE_FUNCTION, true))
			{
				console.log(tKey);
			}
		}
	}
});
crx_registerStructure("MyStructure2",
{
	"VERBOSE": 1,
	"inherits": ["MyStructure1"],
	"shared public var sharedPublicVar2": "MyStructure2::sharedPublicVar2",
	"shared public function sharedPublicFunction": function()
	{
		return "MyStructure2::sharedPublicFunction()";
	},
	"shared private var sharedPrivateVar": "MyStructure2::sharedPrivateVar",
	"shared public function test1": function()
	{
		console.log("Iterating over shared public and shared private variables and functions");
		console.log("---");

		for(tKey in this)
		{
			if(this.HASOWN(tKey, crxOop.HASOWN_SCOPE_SHARED_PUBLIC | crxOop.HASOWN_SCOPE_SHARED_PRIVATE,
					crxOop.HASOWN_TYPE_VAR | crxOop.HASOWN_TYPE_FUNCTION))
			{
				console.log(tKey);
			}
		}

		console.log("---");
		console.log("---");
		console.log("Iterating over shared private and private variables");
		console.log("---");
		
		for(tKey in this)
		{
			if(this.HASOWN(tKey, crxOop.HASOWN_SCOPE_SHARED_PRIVATE | crxOop.HASOWN_SCOPE_PRIVATE,
					crxOop.HASOWN_TYPE_VAR))
			{
				console.log(tKey);
			}
		}

		console.log("---");
		console.log("---");
		console.log("Iterating over all variables and functions of current structure (MyStructure2)");
		console.log("---");
		
		for(tKey in this)
		{
			if(this.HASOWN(tKey, crxOop.HASOWN_SCOPE_SHARED_PUBLIC | crxOop.HASOWN_SCOPE_SHARED_PRIVATE |
					crxOop.HASOWN_SCOPE_PRIVATE, crxOop.HASOWN_TYPE_VAR | crxOop.HASOWN_TYPE_FUNCTION, true))
			{
				console.log(tKey);
			}
		}
	}
});

var gMyStructure = crx_new("MyStructure2");

gMyStructure.test1();
gMyStructure.test2();
Iterating over shared public and shared private variables and functions
---
sharedPrivateFunction
sharedPublicVar
sharedPublicVar2
sharedPrivateVar
sharedPublicFunction
test2
test1
---
---
Iterating over shared private and private variables
---
sharedPrivateVar
---
---
Iterating over all variables and functions of current structure (MyStructure2)
---
sharedPrivateFunction
sharedPublicVar
sharedPublicVar2
sharedPrivateVar
sharedPublicFunction
test2
test1
---
---
Iterating over all variables and functions of current structure (MyStructure1)
---
privateVar
sharedPrivateFunction
sharedPublicVar
sharedPrivateVar
sharedPublicFunction
test2

6.0 Data Typing

CrxOop comes with its own typing system in addition to what javascript provides. Two important functions are provided to complement what javascript provides, crxOop.instanceOf and crxOop.typeOf which complement the native 'instanceof' and 'typeof'. Beyond these two, there are also crxOop.isClassExtending, crxOop.isClassChaining, crxOop.isClassImplementing, crxOop.isStructureInheriting, and crxOop.isStructureContaining.

6.1 crxOop.typeOf

crxOop.typeOf complements the native 'typeof', but CrxOop only understands four types,

  • $CRX_DEFINITION__INTERFACE: An interface definition object.
  • $CRX_DEFINITION__CLASS: A class definition object.
  • $CRX_DEFINITION__STRUCTURE: A structure definition object.
  • $CRX_OBJECT: An instance of a class or a structure.
  • $CRX__native: Every thing else. A native type.
var ExampleInterfaceDefinition =
{
	"function1": 0
};
crx_registerInterface("ExampleInterface", ExampleInterfaceDefinition);

var ExampleClassDefinition =
{
	PUBLIC:
	{
		VARS:
		{
			"publicVar": "I am ExampleClass"
		}
	}
};
crx_registerClass("ExampleClass", ExampleClassDefinition);

var ExampleStructureDefinition = 
{
	SHARED:
	{
		PUBLIC:
		{
			VARS:
			{
				"sharedPublicVar": "I am ExampleStructure"
			}
		}
	}
}
crx_registerStructure("ExampleStructure", ExampleStructureDefinition);

console.log(crxOop.typeOf(ExampleInterfaceDefinition));
console.log(crxOop.typeOf("ExampleInterface"));
console.log(crxOop.typeOf(ExampleClassDefinition));
console.log(crxOop.typeOf("ExampleClass"));
console.log(crxOop.typeOf(ExampleStructureDefinition));
console.log(crxOop.typeOf("ExampleStructure"));
console.log(crxOop.typeOf(crx_new("ExampleClass")));
console.log(crxOop.typeOf(crx_new("ExampleStructure")));
console.log(crxOop.typeOf("a string"));
console.log(crxOop.typeOf(5));
$CRX_DEFINITION__INTERFACE
$CRX__native
$CRX_DEFINITION__CLASS
$CRX__native
$CRX_DEFINITION__STRUCTURE
$CRX__native
$CRX_OBJECT
$CRX_OBJECT
$CRX__native
$CRX__native
var ExampleInterfaceDefinition =
{
	"function1": 0
};
crx_registerInterface("ExampleInterface", ExampleInterfaceDefinition);

var ExampleClassDefinition =
{
	"VERBOSE": 1,
	"public var publicVar": "I am ExampleClass"
};
crx_registerClass("ExampleClass", ExampleClassDefinition);

var ExampleStructureDefinition = 
{
	"VERBOSE": 1,
	"shared public var sharedPublicVar": "I am ExampleStructure"
}
crx_registerStructure("ExampleStructure", ExampleStructureDefinition);

console.log(crxOop.typeOf(ExampleInterfaceDefinition));
console.log(crxOop.typeOf("ExampleInterface"));
console.log(crxOop.typeOf(ExampleClassDefinition));
console.log(crxOop.typeOf("ExampleClass"));
console.log(crxOop.typeOf(ExampleStructureDefinition));
console.log(crxOop.typeOf("ExampleStructure"));
console.log(crxOop.typeOf(crx_new("ExampleClass")));
console.log(crxOop.typeOf(crx_new("ExampleStructure")));
console.log(crxOop.typeOf("a string"));
console.log(crxOop.typeOf(5));
$CRX_DEFINITION__INTERFACE
$CRX__native
$CRX_DEFINITION__CLASS
$CRX__native
$CRX_DEFINITION__STRUCTURE
$CRX__native
$CRX_OBJECT
$CRX_OBJECT
$CRX__native
$CRX__native

Notice how registered names of classes and interface are still of type $CRX__native.

6.2 crxOop.instanceOf

crxOop.instanceOf complements the native 'instanceof'. CrxOop, as mentioned earlier, implements class public inheritance. An instance of ClassC that extends ClassB is also an instance of ClassB, and if ClassB extends ClassA, that instance of ClassC is also an instance of ClassA, and so forth. If ClassA implemented InterfaceA, then the instance of ClassC would also be in 'instance' of InterfaceA, however non castable to InterfaceA.

crx_registerInterface("ExampleInterface1",
{
});
crx_registerClass("ExampleClass1",
{
	IMPLEMENTS: ["ExampleInterface1"]
});

crx_registerClass("ExampleClass2",
{
	EXTENDS: "ExampleClass1"
});

crx_registerInterface("ExampleInterface3",
{
});
crx_registerClass("ExampleClass3",
{
	IMPLEMENTS: ["ExampleInterface3"],
	EXTENDS: "ExampleClass2"
});
vInstance1 = crx_new("ExampleClass1");
vInstance2 = crx_new("ExampleClass2");
vInstance3 = crx_new("ExampleClass3");

console.log((crxOop.instanceOf(vInstance1, "ExampleClass2") ? "true" : "false"));
console.log((crxOop.instanceOf(vInstance2, "ExampleClass1") ? "true" : "false"));
console.log((crxOop.instanceOf(vInstance3, "ExampleClass1") ? "true" : "false"));
console.log((crxOop.instanceOf(vInstance3, "ExampleClass2") ? "true" : "false"));
console.log((crxOop.instanceOf(vInstance3, "ExampleClass3") ? "true" : "false"));
console.log((crxOop.instanceOf(vInstance3.CAST("ExampleClass1"), "ExampleClass3") ? "true" : "false"));
console.log("-----------------------------------");
console.log((crxOop.instanceOf(vInstance1, "ExampleInterface1") ? "true" : "false"));
console.log((crxOop.instanceOf(vInstance2, "ExampleInterface1") ? "true" : "false"));
console.log((crxOop.instanceOf(vInstance3, "ExampleInterface1") ? "true" : "false"));
console.log((crxOop.instanceOf(vInstance3, "ExampleInterface3") ? "true" : "false"));
console.log((crxOop.instanceOf(vInstance3.CAST("ExampleClass1"), "ExampleInterface3") ? "true" : "false"));
false
true
true
true
true
false
-----------------------------------
true
true
true
true
false
crx_registerInterface("ExampleInterface1",
{
});
crx_registerClass("ExampleClass1",
{
	"VERBOSE": 1,
	"implements": ["ExampleInterface1"]
});

crx_registerClass("ExampleClass2",
{
	"VERBOSE": 1,
	"extends": "ExampleClass1"
});

crx_registerInterface("ExampleInterface3",
{
});
crx_registerClass("ExampleClass3",
{
	"VERBOSE": 1,
	"implements": ["ExampleInterface3"],
	"extends": "ExampleClass2"
});
vInstance1 = crx_new("ExampleClass1");
vInstance2 = crx_new("ExampleClass2");
vInstance3 = crx_new("ExampleClass3");

console.log((crxOop.instanceOf(vInstance1, "ExampleClass2") ? "true" : "false"));
console.log((crxOop.instanceOf(vInstance2, "ExampleClass1") ? "true" : "false"));
console.log((crxOop.instanceOf(vInstance3, "ExampleClass1") ? "true" : "false"));
console.log((crxOop.instanceOf(vInstance3, "ExampleClass2") ? "true" : "false"));
console.log((crxOop.instanceOf(vInstance3, "ExampleClass3") ? "true" : "false"));
console.log((crxOop.instanceOf(vInstance3.CAST("ExampleClass1"), "ExampleClass3") ? "true" : "false"));
console.log("-----------------------------------");
console.log((crxOop.instanceOf(vInstance1, "ExampleInterface1") ? "true" : "false"));
console.log((crxOop.instanceOf(vInstance2, "ExampleInterface1") ? "true" : "false"));
console.log((crxOop.instanceOf(vInstance3, "ExampleInterface1") ? "true" : "false"));
console.log((crxOop.instanceOf(vInstance3, "ExampleInterface3") ? "true" : "false"));
console.log((crxOop.instanceOf(vInstance3.CAST("ExampleClass1"), "ExampleInterface3") ? "true" : "false"));
false
true
true
true
true
false
-----------------------------------
true
true
true
true
false

Notice how when vInstance3, an instance of ExampleClass3, was casted to ExampleClass1, it was no longer an instance of ExampleClass3 nor the interface that ExampleClass3 implements.

crxOop.instanceOf can also be used as plain instanceof, however this only works when both parameters are of type $CRX__native, see crxOop.typeOf, and the second parameter is not a string.

7.0 Class And Structure Typing

CrxOop provides minimal support for class and structure reflection which we think should be sufficient for most, if not all, cases, and where it is not sufficient, an alternative better solution to reflection should exist. Seven functions are provided, crxOop.isClassExtending, crxOop.isClassChaining, crxOop.isClassImplementing, crxOop.isClassRegistered, crxOop.isStructureInheriting, crxOop.isStructureContaining, and crxOop.isStructureRegistered. These functions are not meant to reflect on the instances themselves, but instead on class definitions. The idea is to allow a foreign class library for example to work with your own.

7.1 crxOop.isClassExtending

crxOop.isClassExtending can be used to inspect during run time if a class, not an instance, extends another class. If any of the parameters is not a valid class definition object, or registered class name, crxOop.isClassExtending will issue a fatal error.

crx_registerClass("ExampleClass1",
{
});

crx_registerClass("ExampleClass2",
{
	EXTENDS: "ExampleClass1"
});

crx_registerClass("ExampleClass3",
{
	EXTENDS: "ExampleClass2"
});

console.log((crxOop.isClassExtending("ExampleClass1", "ExampleClass2") ? "true" : "false"));
console.log((crxOop.isClassExtending("ExampleClass2", "ExampleClass1") ? "true" : "false"));
console.log((crxOop.isClassExtending("ExampleClass3", "ExampleClass2") ? "true" : "false"));
console.log((crxOop.isClassExtending("ExampleClass3", "ExampleClass3") ? "true" : "false"));
console.log((crxOop.isClassExtending("ExampleClass1", "ExampleClass3") ? "true" : "false"));
false
true
true
false
false
crx_registerClass("ExampleClass1",
{
	"VERBOSE": 1
});

crx_registerClass("ExampleClass2",
{
	"VERBOSE": 1,
	"extends": "ExampleClass1"
});

crx_registerClass("ExampleClass3",
{
	"VERBOSE": 1,
	"extends": "ExampleClass2"
});

console.log((crxOop.isClassExtending("ExampleClass1", "ExampleClass2") ? "true" : "false"));
console.log((crxOop.isClassExtending("ExampleClass2", "ExampleClass1") ? "true" : "false"));
console.log((crxOop.isClassExtending("ExampleClass3", "ExampleClass2") ? "true" : "false"));
console.log((crxOop.isClassExtending("ExampleClass3", "ExampleClass3") ? "true" : "false"));
console.log((crxOop.isClassExtending("ExampleClass1", "ExampleClass3") ? "true" : "false"));
false
true
true
false
false

Notice how a class is not extending itself. If you also want this case to return true, check the next section on crxOop.isClassChaining().

7.2 crxOop.isClassChaining

crxOop.isClassChaining can be used during run time to inspect if pure instances of a class can be casted to another class. The function behaves exactly like crxOop.isClassExtending, but will return true if a class is checked against itself. If any of the parameters is not a valid class definition object, or registered class name, crxOop.isClassChaining will issue a fatal error.

crx_registerClass("ExampleClass1",
{
});

crx_registerClass("ExampleClass2",
{
	EXTENDS: "ExampleClass1"
});

crx_registerClass("ExampleClass3",
{
	EXTENDS: "ExampleClass2"
});

console.log((crxOop.isClassChaining("ExampleClass1", "ExampleClass2") ? "true" : "false"));
console.log((crxOop.isClassChaining("ExampleClass2", "ExampleClass1") ? "true" : "false"));
console.log((crxOop.isClassChaining("ExampleClass3", "ExampleClass2") ? "true" : "false"));
console.log((crxOop.isClassChaining("ExampleClass3", "ExampleClass3") ? "true" : "false"));
console.log((crxOop.isClassChaining("ExampleClass1", "ExampleClass3") ? "true" : "false"));
false
true
true
true
false
crx_registerClass("ExampleClass1",
{
	"VERBOSE": 1
});

crx_registerClass("ExampleClass2",
{
	"VERBOSE": 1,
	"extends": "ExampleClass1"
});

crx_registerClass("ExampleClass3",
{
	"VERBOSE": 1,
	"extends": "ExampleClass2"
});

console.log((crxOop.isClassChaining("ExampleClass1", "ExampleClass2") ? "true" : "false"));
console.log((crxOop.isClassChaining("ExampleClass2", "ExampleClass1") ? "true" : "false"));
console.log((crxOop.isClassChaining("ExampleClass3", "ExampleClass2") ? "true" : "false"));
console.log((crxOop.isClassChaining("ExampleClass3", "ExampleClass3") ? "true" : "false"));
console.log((crxOop.isClassChaining("ExampleClass1", "ExampleClass3") ? "true" : "false"));
false
true
true
true
false

7.3 crxOop.isClassImplementing

crxOop.isClassImplementing can be used to check during run time whether a class implements a particular interface or not. If the first parameter is not a valid class definition object, or registered class name, or the second parameter is not a valid interface definition object, or registered interface name, crxOop.isClassImplementing will issue a fatal error.

crx_registerInterface("ExampleInterface1b",
{
});
crx_registerInterface("ExampleInterface1",
{
	INHERITS: ["ExampleInterface1b"]
});
crx_registerClass("ExampleClass1",
{
	IMPLEMENTS: ["ExampleInterface1"]
});

crx_registerClass("ExampleClass2",
{
	EXTENDS: "ExampleClass1"
});

crx_registerInterface("ExampleInterface3",
{
});
crx_registerClass("ExampleClass3",
{
	IMPLEMENTS: ["ExampleInterface3"],
	EXTENDS: "ExampleClass2"
});

console.log((crxOop.isClassImplementing("ExampleClass1", "ExampleInterface1") ? "true" : "false"));
console.log((crxOop.isClassImplementing("ExampleClass1", "ExampleInterface1b") ? "true" : "false"));
console.log((crxOop.isClassImplementing("ExampleClass1", "ExampleInterface3") ? "true" : "false"));
console.log((crxOop.isClassImplementing("ExampleClass2", "ExampleInterface1") ? "true" : "false"));
console.log((crxOop.isClassImplementing("ExampleClass3", "ExampleInterface3") ? "true" : "false"));
console.log((crxOop.isClassImplementing("ExampleClass3", "ExampleInterface1") ? "true" : "false"));
console.log((crxOop.isClassImplementing("ExampleClass3", "ExampleInterface1b") ? "true" : "false"));
true
true
false
true
true
true
true
crx_registerInterface("ExampleInterface1b",
{
});
crx_registerInterface("ExampleInterface1",
{
	INHERITS: ["ExampleInterface1b"]
});
crx_registerClass("ExampleClass1",
{
	"VERBOSE": 1,
	"implements": ["ExampleInterface1"]
});

crx_registerClass("ExampleClass2",
{
	"VERBOSE": 1,
	"extends": "ExampleClass1"
});

crx_registerInterface("ExampleInterface3",
{
});
crx_registerClass("ExampleClass3",
{
	"VERBOSE": 1,
	"implements": ["ExampleInterface3"],
	"extends": "ExampleClass2"
});

console.log((crxOop.isClassImplementing("ExampleClass1", "ExampleInterface1") ? "true" : "false"));
console.log((crxOop.isClassImplementing("ExampleClass1", "ExampleInterface1b") ? "true" : "false"));
console.log((crxOop.isClassImplementing("ExampleClass1", "ExampleInterface3") ? "true" : "false"));
console.log((crxOop.isClassImplementing("ExampleClass2", "ExampleInterface1") ? "true" : "false"));
console.log((crxOop.isClassImplementing("ExampleClass3", "ExampleInterface3") ? "true" : "false"));
console.log((crxOop.isClassImplementing("ExampleClass3", "ExampleInterface1") ? "true" : "false"));
console.log((crxOop.isClassImplementing("ExampleClass3", "ExampleInterface1b") ? "true" : "false"));
true
true
false
true
true
true
true

Notice how crxOop.isClassImplementing continues to return true if not the class itself, but a class in its extension chain implements the interface.

7.4 crxOop.isClassRegistered

crxOop.isClassRegistered can be used to check during run time whether a class has been registered or not. The first parameter can be either a class definition object (object), or a class name (string).

crx_registerClass("ExampleClass1",
{
});

crx_registerClass("ExampleClass2",
{
});

console.log((crxOop.isClassRegistered("ExampleClass1") ? "true" : "false"));
console.log((crxOop.isClassRegistered("ExampleClass2") ? "true" : "false"));
console.log((crxOop.isClassRegistered("ExampleClass3") ? "true" : "false"));
true
true
false
crx_registerClass("ExampleClass1",
{
	"VERBOSE": 1
});

crx_registerClass("ExampleClass2",
{
	"VERBOSE": 1
});

console.log((crxOop.isClassRegistered("ExampleClass1") ? "true" : "false"));
console.log((crxOop.isClassRegistered("ExampleClass2") ? "true" : "false"));
console.log((crxOop.isClassRegistered("ExampleClass3") ? "true" : "false"));
true
true
false

7.5 crxOop.isStructureInheriting

crxOop.isStructureInheriting can be used to inspect during run time if a structure, not an instance, inherits another structure. If any of the parameters is not a valid structure definition object, or registered structure name, crxOop.isStructureInheriting will issue a fatal error.

crx_registerStructure("ExampleStructure1",
{
});

crx_registerStructure("ExampleStructure2",
{
	INHERITS: ["ExampleStructure1"]
});

crx_registerStructure("ExampleStructure3",
{
	INHERITS: ["ExampleStructure2"]
});

console.log((crxOop.isStructureInheriting("ExampleStructure1", "ExampleStructure2") ? "true" : "false"));
console.log((crxOop.isStructureInheriting("ExampleStructure2", "ExampleStructure1") ? "true" : "false"));
console.log((crxOop.isStructureInheriting("ExampleStructure3", "ExampleStructure2") ? "true" : "false"));
console.log((crxOop.isStructureInheriting("ExampleStructure3", "ExampleStructure3") ? "true" : "false"));
console.log((crxOop.isStructureInheriting("ExampleStructure1", "ExampleStructure3") ? "true" : "false"));
false
true
true
false
false
crx_registerStructure("ExampleStructure1",
{
	"VERBOSE": 1
});

crx_registerStructure("ExampleStructure2",
{
	"VERBOSE": 1,
	INHERITS: ["ExampleStructure1"]
});

crx_registerStructure("ExampleStructure3",
{
	"VERBOSE": 1,
	INHERITS: ["ExampleStructure2"]
});

console.log((crxOop.isStructureInheriting("ExampleStructure1", "ExampleStructure2") ? "true" : "false"));
console.log((crxOop.isStructureInheriting("ExampleStructure2", "ExampleStructure1") ? "true" : "false"));
console.log((crxOop.isStructureInheriting("ExampleStructure3", "ExampleStructure2") ? "true" : "false"));
console.log((crxOop.isStructureInheriting("ExampleStructure3", "ExampleStructure3") ? "true" : "false"));
console.log((crxOop.isStructureInheriting("ExampleStructure1", "ExampleStructure3") ? "true" : "false"));
false
true
true
false
false

Notice how a structure is not inheriting itself. If you also want this case to return true, check the next section on crxOop.isStructureContaining().

7.6 crxOop.isStructureContaining

crxOop.isStructureContaining can be used during run time to inspect if pure instances of a structure are a super set of another structure. The function behaves exactly like crxOop.isStructureInheriting, but will return true if a structure is checked against itself. If any of the parameters is not a valid structure definition object, or registered structure name, crxOop.isStructureContaining will issue a fatal error.

crx_registerStructure("ExampleStructure1",
{
});

crx_registerStructure("ExampleStructure2",
{
	INHERITS: ["ExampleStructure1"]
});

crx_registerStructure("ExampleStructure3",
{
	INHERITS: ["ExampleStructure2"]
});

console.log((crxOop.isStructureContaining("ExampleStructure1", "ExampleStructure2") ? "true" : "false"));
console.log((crxOop.isStructureContaining("ExampleStructure2", "ExampleStructure1") ? "true" : "false"));
console.log((crxOop.isStructureContaining("ExampleStructure3", "ExampleStructure2") ? "true" : "false"));
console.log((crxOop.isStructureContaining("ExampleStructure3", "ExampleStructure3") ? "true" : "false"));
console.log((crxOop.isStructureContaining("ExampleStructure1", "ExampleStructure3") ? "true" : "false"));
false
true
true
true
false
crx_registerStructure("ExampleStructure1",
{
	"VERBOSE": 1
});

crx_registerStructure("ExampleStructure2",
{
	"VERBOSE": 1,
	INHERITS: ["ExampleStructure1"]
});

crx_registerStructure("ExampleStructure3",
{
	"VERBOSE": 1,
	INHERITS: ["ExampleStructure2"]
});

console.log((crxOop.isStructureContaining("ExampleStructure1", "ExampleStructure2") ? "true" : "false"));
console.log((crxOop.isStructureContaining("ExampleStructure2", "ExampleStructure1") ? "true" : "false"));
console.log((crxOop.isStructureContaining("ExampleStructure3", "ExampleStructure2") ? "true" : "false"));
console.log((crxOop.isStructureContaining("ExampleStructure3", "ExampleStructure3") ? "true" : "false"));
console.log((crxOop.isStructureContaining("ExampleStructure1", "ExampleStructure3") ? "true" : "false"));
false
true
true
true
false

7.7 crxOop.isStructureRegistered

crxOop.isStructureRegistered can be used to check during run time whether a structure has been registered or not. The first parameter can be either a structure definition object (object), or a structure name (string).

crx_registerStructure("ExampleStructure1",
{
});

crx_registerStructure("ExampleStructure2",
{
});

console.log((crxOop.isStructureRegistered("ExampleStructure1") ? "true" : "false"));
console.log((crxOop.isStructureRegistered("ExampleStructure2") ? "true" : "false"));
console.log((crxOop.isStructureRegistered("ExampleStructure3") ? "true" : "false"));
true
true
false
crx_registerStructure("ExampleStructure1",
{
	"VERBOSE": 1
});

crx_registerStructure("ExampleStructure2",
{
	"VERBOSE": 1
});

console.log((crxOop.isStructureRegistered("ExampleStructure1") ? "true" : "false"));
console.log((crxOop.isStructureRegistered("ExampleStructure2") ? "true" : "false"));
console.log((crxOop.isStructureRegistered("ExampleStructure3") ? "true" : "false"));
true
true
false

8.0 Security

CrxOop uses javascript features to provide the OOP features CrxOop provides. It is not a new language, and hence, it is susceptable to javascript's own features, whether good or bad. The 'instances' CrxOop creates are just javascript objects, and unless you are careful about what you are doing, the effect could break, which will break the integrity of the application, and break security when it comes to secure applications.

CrxOop instances, as mentioned earlier, are made of private memory sectors, and public memory sectors. The private sectors contain the class members declared as private, and the public sectors contain the class members declared as public. In other words, unlike other languages where private and public accessors are enforced by the compiler or interpretor, CrxOop's enforcement happens during runtime by carefull memory layout.

Two issues are to be kept in mind. One, the built instances could be modified during runtime, changing them from the class imprints that they are made from. Imagine passing a password to a secure function on an instnace, where the function is not the function you think it is. This issue is resolved by CrxOop using javascript's own object locking mechanisms. Needless to say, older browsers, roughly IE8 era, might suffer. However, exploiting this on the older browsers is not an easy matter, and with carefull programming should be impossible. In our example, making the secure function private is all that is needed. Please also see the section on CrxOop modes.

The second issue is leaking of the private memory sectors of an instance. This can happen through either passing of dangerous Class Keywords, including 'this', to other functions. Please see the section on Class Keywords for more information. Or infestation of the private sector by assigning a malicious function to a class private instance variable. Consider the following:

crx_registerClass("ExampleClass",
{
	PRIVATE:
	{
		VARS:
		{
			"privateVar1": "privateVar1",
			"privateVar2": null
		}
	},
	PUBLIC:
	{
		FUNCTIONS:
		{
			"setPrivateVar2": function(pFunction)
			{
				this.privateVar2 = pFunction;
			},
			"printPrivateVar": function()
			{
				console.log(this.privateVar1);
			},
			"test": function()
			{
				this.privateVar2();
			}
		}
	}
});

var vInstance = crx_new("ExampleClass");

vInstance.printPrivateVar();

//START: ATTACKER CODE
function maliciousCode()
	{console.log(this.privateVar1);}

vInstance.setPrivateVar2(maliciousCode);
//END: ATTACKER CODE

vInstance.test();
privateVar1
privateVar1
crx_registerClass("ExampleClass",
{
	"VERBOSE": 1,
	"private var privateVar1": "privateVar1",
	"private var privateVar2": null,
	"public function setPrivateVar2": function(pFunction)
	{
		this.privateVar2 = pFunction;
	},
	"public function printPrivateVar": function()
	{
		console.log(this.privateVar1);
	},
	"public function test": function()
	{
		this.privateVar2();
	}
});

var vInstance = crx_new("ExampleClass");

vInstance.printPrivateVar();

//START: ATTACKER CODE
function maliciousCode()
	{console.log(this.privateVar1);}

vInstance.setPrivateVar2(maliciousCode);
//END: ATTACKER CODE

vInstance.test();
privateVar1
privateVar1

Notice how the malicious function maliciousCode() managed to read the private variable privateVar1, and all it took was the reading of "this". Very serious, but maybe not so because exploiting this can be difficult. The attacker still needs to access the private variable where his malicious code was stored, which in our case was conveniently executed by the test function. However, this author does not know if there are other ways to exploit this, but one thing for certain, if not a security risk, this can lead to bad habbits. Avoid exploiting this yourself on your own classes to try and change the meaning of accessors. You might think I shall use this to mimick class friends for example. Avoid that, and remember that CrxOop does not support nor accounts for you doing this.

When it comes to security, despite the above skepticism, do not play with fire. And when it comes to integrity, avoid exploiting this with your own code to keep code well defined and clean. To stop such things, CrxOop provides the function crxOop.var(). crxOop.var takes a single argument, and if it is a function returns a safe modification of the function, and if not returns the argument as it is.

crx_registerClass("ExampleClass",
{
	PRIVATE:
	{
		VARS:
		{
			"privateVar1": "privateVar1",
			"privateVar2": null
		}
	},
	PUBLIC:
	{
		FUNCTIONS:
		{
			"setPrivateVar2": function(pFunction)
			{
				//SECURITY
				this.privateVar2 = crxOop.var(pFunction);
			},
			"printPrivateVar": function()
			{
				console.log(this.privateVar1);
			},
			"test": function()
			{
				this.privateVar2();
			}
		}
	}
});

var vInstance = crx_new("ExampleClass");

vInstance.printPrivateVar();

//START: ATTACKER CODE
function maliciousCode()
	{console.log(this.privateVar1);}

vInstance.setPrivateVar2(maliciousCode);
//END: ATTACKER CODE

vInstance.test();
privateVar1
undefined
crx_registerClass("ExampleClass",
{
	"VERBOSE": 1,
	"private var privateVar1": "privateVar1",
	"private var privateVar2": null,
	"public function setPrivateVar2": function(pFunction)
	{
		//SECURITY
		this.privateVar2 = crxOop.var(pFunction);
	},
	"public function printPrivateVar": function()
	{
		console.log(this.privateVar1);
	},
	"public function test": function()
	{
		this.privateVar2();
	}
});

var vInstance = crx_new("ExampleClass");

vInstance.printPrivateVar();

//START: ATTACKER CODE
function maliciousCode()
	{console.log(this.privateVar1);}

vInstance.setPrivateVar2(maliciousCode);
//END: ATTACKER CODE

vInstance.test();
privateVar1
undefined

The code works now as expected. crxOop.var simply bind functions' 'this' to something safe. You can use javascripts own facilities if you like, but the advantage to crxOop.var is that it works with older browsers, and it makes the functions work with CrxOop's internal halt signal. This means, if there is a fatal error issued by CrxOop, those functions will also halt.

8.1 Security

This is a summary, for easy quick reference, of rules to follow to avoid security issues with CrxOop.

  • The class keywords are 'O', 'this', 'THIS', 'PARENT', 'CONSTRUCT', 'VF', 'STATIC' AND 'CAST'. Class keywords must never be passed around to functions as parameters, or from functions as returns, apart from 'THIS' and 'PARENT'.
  • The class keywords 'O', 'VF', 'STATIC' AND 'CAST' are functions and are functions that have a return. Apart from 'CAST', the return of these keywords must never be passed around to functions as parameters, or from functions as returns.
  • The global keyword 'crx_static', a function, as well as all global keywords is safe to pass around. However, the return of 'crx_static' is not safe to pass to functions, or as returns from functions, when a call such as crx_static('classX') is made inside a static function belonging to classX. crx_static is context sensitive, and in the case mentioned earlier would return a pointer giving access to private memory.
  • For assigning variables from untrusted sources to class members, use crxOop.var().

9.0 License

License

The MIT License (MIT)

Copyright (c) 2016 ObIb

ObIb: Ob are the first two letters of my first name, and Ib are the first two letters of my father's father's (grandfather's) first name. I was the senior developer at Creatrix Design Group (website: creatrix.ca. address:2764 Richmond Rd, Ottawa Ontario Canada K2B6S2), during but not limited to the year 2014. This information is part of the license and uniquely identifies me, the license holder.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

















d