From 92c57c8ffe081da347163b6a4fb17b274eee5e94 Mon Sep 17 00:00:00 2001 From: Mike Gerwitz Date: Mon, 2 Jan 2017 23:28:28 -0500 Subject: [PATCH] Constructor virtual by default * lib/ClassBuilder.js (_keywordParser): Make __construct virtual. * test/Class/ConstructorTest.js: Add test. * doc/classes.texi (Constructors): Update documentation. --- doc/classes.texi | 19 +++++++++++++++++++ lib/ClassBuilder.js | 14 ++++++++++---- test/Class/ConstructorTest.js | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 4 deletions(-) diff --git a/doc/classes.texi b/doc/classes.texi index 809416e..673b7d6 100644 --- a/doc/classes.texi +++ b/doc/classes.texi @@ -568,6 +568,25 @@ Constructors are always public; It is not permitted to make a constructor protected or private (@pxref{Access Modifiers}). +Unlike all other methods, + constructors are @ref{Member Keywords,,@code{virtual}} by default. +Many other languages (C++, Java, C#, and others) do not inherit + class constructors from their supertypes. +ease.js classes are prototypes, + and uninstantiated prototypes are functions (constructors), + so classes are effectively first-class objects in JavaScript. +Consequently, + they can be passed around and invoked like any other function, + which can be a convenient alternative to factories + (and a transparent alternative to functions that create objects). +It is therefore useful to have the constructor as part of the class's + public API. +However, + it is also important that subtypes always be able to override the + constructor (@pxref{Overriding Methods}); + otherwise subtypes may not be able to initialize properly, + making for a very clumsy implementation. + Constructors are optional, and no constructor is defined by default.@footnote{ That is, no user-facing constructor is defined by default; diff --git a/lib/ClassBuilder.js b/lib/ClassBuilder.js index 68de6a3..31036b7 100644 --- a/lib/ClassBuilder.js +++ b/lib/ClassBuilder.js @@ -1,7 +1,8 @@ /** * Handles building of classes * - * Copyright (C) 2011, 2012, 2013, 2014, 2015, 2016 Free Software Foundation, Inc. + * Copyright (C) 2011, 2012, 2013, 2014, 2015, 2016, 2017 + * Free Software Foundation, Inc. * * This file is part of GNU ease.js. * @@ -687,9 +688,8 @@ exports.prototype.buildMembers = function buildMembers( /** * Member keyword parser * - * In reality, this parser is simply intended to override names where there - * are applicable aliases; all keyword parsing is kept to the original - * implementation. + * This parser handles aliases and constructor virtualization; all keyword + * parsing is kept to the original implementation. * * @param {string} prop property to parse * @@ -705,6 +705,12 @@ function _keywordParser( prop ) result.name = alias; } + // constructors are always virtual by default (exception to the rule) + if ( result.name === '__construct' ) + { + result.keywords[ 'virtual' ] = true; + } + return result; } diff --git a/test/Class/ConstructorTest.js b/test/Class/ConstructorTest.js index d382870..176a89b 100644 --- a/test/Class/ConstructorTest.js +++ b/test/Class/ConstructorTest.js @@ -218,6 +218,39 @@ require( 'common' ).testCase( }, + /** + * This one is a bit of an interesting case. Information can be found + * in the manual, but for the sake of this test, all we need to know is + * that we should be able to override `__construct' without having + * provided the `virtual' keyword on the supertype. This differs from + * all other methods which are non-virtual by default. + */ + '@each(ctors) Constructor is virtual by default': function( name ) + { + var _self = this; + + this.assertDoesNotThrow( function() + { + var sub_called = false; + + // not explicitly virtual + var base_dfn = {}; + base_dfn[ name ] = function() {}; + + var sub_dfn = {}; + sub_dfn[ 'override ' + name ] = function() + { + sub_called = true; + }; + + _self.Sut.extend( _self.Sut( base_dfn ), sub_dfn )(); + + // sanity check + _self.assertOk( sub_called ); + }, Error ); + }, + + /** * When a constructor is instantiated conventionally in ECMAScript, the * instance's `constructor' property is set to the constructor that was