CrxOop: Bringing Object Oriented Programming To Javascript
The aim of the documentation 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. It describes the available features and their syntax.

CrxOop: Bringing Object Oriented Programming To Javascript

Table of contents

1.1 Preface (IMPORTANT)

CrxOop is a javascript (JS) library that should 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.

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 Syntax Overview

CrxOop 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 protected access and 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: 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
{
	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->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;
									
};
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";
	}
};
int main()
{
	classC* a = new classC();
									
	a->test(5);
	((classA*)a)->test(5);
	classA::publicStaticFunction((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()
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()
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",
{
	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.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
									
			}
		}
	}
});
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()");
				}
			}
		}
	}
});

var a = crx_new("classC");

a.test(5);
a.CAST("classA").test(5);
crx_static("classA").publicStaticFunction(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()
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()
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,
	"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.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
									
});
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()");
	}
});
									
var a = crx_new("classC");
									
a.test(5);
a.CAST("classA").test(5);
crx_static("classA").publicStaticFunction(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()
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()

#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
{
                  

crx_registerClass("classA",
{
                  

  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->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.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

                  

};
                  

      }
    }
  }
});
                  

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()");
        }
      }
    }
  }
                  

};
                  

});
                  

int main()
{
                  

  classC* a = new classC();

                  


var a = crx_new("classC");
                  

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


a.test(5);
a.CAST("classA").test(5);
crx_static("classA").publicStaticFunction(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()
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()
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()
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()

#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
{
                  

crx_registerClass("classA",
{
  "VERBOSE": 1,
                  

  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->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.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

                  

};
                  

});
                  

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()");
  }
                  

};
                  

});

                  

int main()
{
                  

  classC* a = new classC();

                  

var a = crx_new("classC");

                  

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

a.test(5);
a.CAST("classA").test(5);
crx_static("classA").publicStaticFunction(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()
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()
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()
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()

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, 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, AND CAST.

    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.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.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 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 and interface 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 and interfaces 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.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)
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 Keywords

3.5.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.5.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. 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",
{
	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 pExample, 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);
			}
		}
	}
});

var instance = crx_new("ExampleClass2");

instance.test();
privateVar
I am ExampleClass1's publicVar
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(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 pExample, 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);
	}
});

var instance = crx_new("ExampleClass2");

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

3.5.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.5.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.5.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.5.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.5.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

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 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 and crxOop_isClassImplementing.

5.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_OBJECT: An instance of a class.
  • $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);

console.log(crxOop_typeof(ExampleInterfaceDefinition));
console.log(crxOop_typeof("ExampleInterface"));
console.log(crxOop_typeof(ExampleClassDefinition));
console.log(crxOop_typeof("ExampleClass"));
console.log(crxOop_typeof(crx_new("ExampleClass")));
console.log(crxOop_typeof("a string"));
console.log(crxOop_typeof(5));
$CRX_DEFINITION__INTERFACE
$CRX__native
$CRX_DEFINITION__CLASS
$CRX__native
$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);

console.log(crxOop_typeof(ExampleInterfaceDefinition));
console.log(crxOop_typeof("ExampleInterface"));
console.log(crxOop_typeof(ExampleClassDefinition));
console.log(crxOop_typeof("ExampleClass"));
console.log(crxOop_typeof(crx_new("ExampleClass")));
console.log(crxOop_typeof("a string"));
console.log(crxOop_typeof(5));
$CRX_DEFINITION__INTERFACE
$CRX__native
$CRX_DEFINITION__CLASS
$CRX__native
$CRX_OBJECT
$CRX__native
$CRX__native

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

5.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.

6.0 Class Typing

CrxOop provides minimal support for class 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. Three functions are provided, crxOop_isClassExtending, crxOop_isClassChaining, and crxOop_isClassImplementing 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.

6.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().

6.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

6.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.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();
privateVar
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();
privateVar
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.

7.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().

8.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