A new feature worth mentioning is support for inheritance (I mean Javascript-style, "prototypical inheritance") in both directions. Suppose the following hierarchy:
First, here is Shape the "superclass", defined in Javascript:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function Shape(x, y) { | |
this.x = x; | |
this.y = y; | |
} | |
Shape.prototype.move = function(x, y) { | |
this.x += x; | |
this.y += y; | |
}; |
Next comes Circle, a "subclass" of Shape, defined in Javascript as well:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function Circle(x, y, r) { | |
Shape.call(this, x, y); | |
this.r = r; | |
} | |
Circle.prototype = Object.create(Shape.prototype); | |
Circle.prototype.constructor = Circle; |
The following is Rectangle, defined in Pascal, as a "subclass" of Shape:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
type | |
TRectangle = class(TNativeObject) | |
class procedure InitializeInstance(AInstance: JsValueRef; Args: PJsValueRef; ArgCount: Word); override; | |
class function InitializePrototype(AConstructor: JsValueRef): JsValueRef; override; | |
end; | |
class procedure TRectangle.InitializeInstance(AInstance: JsValueRef; Args: PJsValueRef; ArgCount: Word); | |
var | |
ArgsArray: PJsValueRefArray absolute Args; | |
ShapeCtr: JsValueRef; | |
begin | |
// Shape.call(x, y); | |
ShapeCtr := JsGetProperty(JsGlobal, 'Shape'); | |
JsCallFunction(ShapeCtr, [AInstance, ArgsArray^[0], ArgsArray^[1]]); | |
// this.w = w; | |
JsSetProperty(AInstance, UnicodeString('w'), ArgsArray^[2]); | |
// this.h = h; | |
JsSetProperty(AInstance, UnicodeString('h'), ArgsArray^[3]); | |
end; | |
class function TRectangle.InitializePrototype(AConstructor: JsValueRef): JsValueRef; | |
var | |
ShapeCtr, ShapePrototype: JsValueRef; | |
begin | |
ShapeCtr := JsGetProperty(JsGlobal, 'Shape'); | |
ShapePrototype := JsGetProperty(ShapeCtr, 'prototype'); | |
// Rectangle.prototype = Object.create(Shape.prototype); | |
Result := JsCreateObject(ShapePrototype); | |
end; |
Here's Square, defined in Javascript again, as a "subclass" of Rectangle:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function Square(x, y, w) { | |
Rectangle.call(this, x, y, w, w); | |
} | |
Square.prototype = Object.create(Rectangle.prototype); | |
Square.prototype.constructor = Square; |
And finally here are the tests, demonstrating that
- (Javascript) Circle descends from (Javascript) Shape
- (Pascal) Rectangle descends from (Javascript) Shape
- (Javascript) Square descends from (Pascal) Rectangle
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
procedure TNativeClassTestCase.TestInheritance; | |
// - Shape (x, y) => Object | |
// - Circle (x, y, r) => Shape (x, y) | |
// - Rectangle (x, y, w, h) => Shape (x, y) | |
// - Square (x, y, w) => Rectangle (x, y, w, w) | |
Runtime: TChakraCoreRuntime; | |
Context: TChakraCoreContext; | |
ShapeObj, CircleObj, RectangleObj, SquareObj: JsValueRef; | |
begin | |
Runtime := nil; | |
Context := nil; | |
try | |
Runtime := TChakraCoreRuntime.Create([]); | |
Context := TChakraCoreContext.Create(Runtime); | |
Context.Activate; | |
TRectangle.Project('Rectangle'); | |
JsRunScript(SScript, SName); | |
// var shapeObj = new Shape(10, 10); | |
ShapeObj := JsNew('Shape', [IntToJsNumber(10), IntToJsNumber(10)]); | |
CheckValueType(JsObject, ShapeObj, 'shapeObj value type'); | |
CheckTrue(JsInstanceOf(ShapeObj, 'Shape'), 'shapeObj instanceof Shape'); | |
CheckFalse(JsInstanceOf(ShapeObj, 'Circle'), 'shapeObj instanceof Circle'); | |
CheckFalse(JsInstanceOf(ShapeObj, 'Rectangle'), 'shapeObj instanceof Rectangle'); | |
CheckFalse(JsInstanceOf(ShapeObj, 'Square'), 'shapeObj instanceof Square'); | |
CheckEquals(10, JsNumberToInt(JsGetProperty(ShapeObj, 'x')), 'shapeObj.x before move'); | |
CheckEquals(10, JsNumberToInt(JsGetProperty(ShapeObj, 'y')), 'shapeObj.y before move'); | |
JsCallFunction('move', [IntToJsNumber(10), IntToJsNumber(10)], ShapeObj); | |
CheckEquals(20, JsNumberToInt(JsGetProperty(ShapeObj, 'x')), 'shapeObj.x after move'); | |
CheckEquals(20, JsNumberToInt(JsGetProperty(ShapeObj, 'y')), 'shapeObj.y after move'); | |
// var circleObj = new Circle(10, 10, 10); | |
CircleObj := JsNew('Circle', [IntToJsNumber(10), IntToJsNumber(10), IntToJsNumber(10)]); | |
CheckValueType(JsObject, CircleObj, 'circleObj value type'); | |
CheckTrue(JsInstanceOf(CircleObj, 'Shape'), 'circleObj instanceof Shape'); | |
CheckTrue(JsInstanceOf(CircleObj, 'Circle'), 'circleObj instanceof Circle'); | |
CheckFalse(JsInstanceOf(CircleObj, 'Rectangle'), 'circleObj instanceof Rectangle'); | |
CheckFalse(JsInstanceOf(CircleObj, 'Square'), 'circleObj instanceof Square'); | |
CheckEquals(10, JsNumberToInt(JsGetProperty(CircleObj, 'x')), 'circleObj.x before move'); | |
CheckEquals(10, JsNumberToInt(JsGetProperty(CircleObj, 'y')), 'circleObj.y before move'); | |
JsCallFunction('move', [IntToJsNumber(10), IntToJsNumber(10)], CircleObj); | |
CheckEquals(20, JsNumberToInt(JsGetProperty(CircleObj, 'x')), 'circleObj.x after move'); | |
CheckEquals(20, JsNumberToInt(JsGetProperty(CircleObj, 'y')), 'circleObj.y after move'); | |
// var rectangleObj = new Rectangle(10, 10, 60, 40); | |
RectangleObj := JsNew('Rectangle', [IntToJsNumber(10), IntToJsNumber(10), IntToJsNumber(60), IntToJsNumber(40)]); | |
CheckValueType(JsObject, RectangleObj, 'rectangleObj value type'); | |
CheckTrue(JsInstanceOf(RectangleObj, 'Shape'), 'rectangleObj instanceof Shape'); | |
CheckFalse(JsInstanceOf(RectangleObj, 'Circle'), 'rectangleObj instanceof Circle'); | |
CheckTrue(JsInstanceOf(RectangleObj, 'Rectangle'), 'rectangleObj instanceof Rectangle'); | |
CheckFalse(JsInstanceOf(RectangleObj, 'Square'), 'rectangleObj instanceof Square'); | |
CheckEquals(10, JsNumberToInt(JsGetProperty(RectangleObj, 'x')), 'rectangleObj.x before move'); | |
CheckEquals(10, JsNumberToInt(JsGetProperty(RectangleObj, 'y')), 'rectangleObj.y before move'); | |
JsCallFunction('move', [IntToJsNumber(10), IntToJsNumber(10)], RectangleObj); | |
CheckEquals(20, JsNumberToInt(JsGetProperty(RectangleObj, 'x')), 'rectangleObj.x after move'); | |
CheckEquals(20, JsNumberToInt(JsGetProperty(RectangleObj, 'y')), 'rectangleObj.y after move'); | |
// var squareObj = new Square(10, 10, 20); | |
SquareObj := JsNew('Square', [IntToJsNumber(10), IntToJsNumber(10), IntToJsNumber(20)]); | |
CheckValueType(JsObject, SquareObj, 'squareObj value type'); | |
CheckTrue(JsInstanceOf(SquareObj, 'Shape'), 'squareObj instanceof Shape'); | |
CheckFalse(JsInstanceOf(SquareObj, 'Circle'), 'squareObj instanceof Circle'); | |
CheckTrue(JsInstanceOf(SquareObj, 'Rectangle'), 'squareObj instanceof Rectangle'); | |
CheckTrue(JsInstanceOf(SquareObj, 'Square'), 'squareObj instanceof Square'); | |
CheckEquals(10, JsNumberToInt(JsGetProperty(SquareObj, 'x')), 'squareObj.x before move'); | |
CheckEquals(10, JsNumberToInt(JsGetProperty(SquareObj, 'y')), 'sqaureObj.y before move'); | |
JsCallFunction('move', [IntToJsNumber(10), IntToJsNumber(10)], SquareObj); | |
CheckEquals(20, JsNumberToInt(JsGetProperty(SquareObj, 'x')), 'squareObj.x after move'); | |
CheckEquals(20, JsNumberToInt(JsGetProperty(SquareObj, 'y')), 'squareObj.y after move'); | |
finally | |
Context.Free; | |
Runtime.Free; | |
end; | |
end; |
As always, you can grab the latest code on GitHub. Cheers!