From 077eda6fb34d1acb9c758838a404a28e5cf33974 Mon Sep 17 00:00:00 2001 From: "Alex A. Naanou" Date: Wed, 7 Dec 2022 15:47:24 +0300 Subject: [PATCH] added ASIS(..) wrapper to help prevent values returned by .__new__(..) from being intergrated into the current object prototype chain enabling constructors to return non-instance values. Signed-off-by: Alex A. Naanou --- README.md | 13 +++++++++++++ object.js | 20 ++++++++++++++++++-- package.json | 2 +- test.js | 30 +++++++++++++++++++++++++++++- 4 files changed, 61 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 12db264..a45f97d 100755 --- a/README.md +++ b/README.md @@ -130,6 +130,7 @@ class B extends A { - [`.__call__(..)`](#object__call__) - [Components](#components) - [`STOP` / `STOP(..)`](#stop--stop) + - [`ASIS(..)`](#asis) - [`Constructor(..)` / `C(..)`](#constructor--c) - [`create(..)` / `Constructor.create(..)`](#create--constructorcreate) - [`sources(..)` / `Constructor.sources(..)`](#sources--constructorsources) @@ -508,6 +509,13 @@ no instance exists yet. `` is the _outer_ context of the call, i.e. the object from which `` was referenced before it was called. +Any value returned by `.__new__(..)` will be integrated into the prototype +chain of ``, if this is not desired then wrap it in +[`object.ASIS(..)`](#asis) before returning, but note that this will +not prevent `.__init__(..)` from being called. The `ASIS(..)`-wrapped +value will be unwrapped before being returned by the constructor. + + For more info see: - [Low level constructor](#low-level-constructor), - [Inheriting from native constructor objects](#inheriting-from-native-constructor-objects) @@ -564,6 +572,11 @@ to stop the search before it reaches the top of the prototype chain. +### `ASIS(..)` + +Can be used in [`.__new__(..)`](#object__new__) to wrap the returned object to +prevent changing it's prototype by [`RawInstance()`](#rawinstance). + ### `Constructor(..)` / `C(..)` diff --git a/object.js b/object.js index d26a8a7..d25f69c 100755 --- a/object.js +++ b/object.js @@ -241,6 +241,15 @@ function(obj){ //--------------------------------------------------------------------- // Helper objects/constructors... +var ASIS = +module.ASIS = +function ASIS(value){ + return { + __proto__: ASIS.prototype, + value, + } } + + BOOTSTRAP(function(){ // Error with some JS quirks fixed... @@ -647,6 +656,10 @@ function(context, constructor, ...args){ // default object base... : Reflect.construct(Object, [], constructor) + // return wrapped object as-is... + if(obj instanceof module.ASIS){ + return obj.value } + // link to prototype chain, if not done already... obj.__proto__ !== constructor.prototype && (obj.__proto__ = constructor.prototype) @@ -917,8 +930,11 @@ function Constructor(name, a, b, c){ _constructor.__rawinstance__(this, ...arguments) : RawInstance(this, _constructor, ...arguments) // initialize... - obj.__init__ instanceof Function - && obj.__init__(...arguments) + // NOTE: we are using _constructor.prototype.__init__ here instead + // of obj.__init__ as at this point object may be of a different + // type (see RawInstance + ASIS)... + _constructor.prototype.__init__ instanceof Function + && _constructor.prototype.__init__.call(obj, ...arguments) return obj } // rename the consructor... // NOTE: we are not using: diff --git a/package.json b/package.json index eaef069..3d28562 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ig-object", - "version": "6.2.0", + "version": "6.2.1", "description": "", "main": "object.js", "scripts": { diff --git a/test.js b/test.js index 6251833..9a1c844 100755 --- a/test.js +++ b/test.js @@ -205,7 +205,7 @@ var setups = test.Setups({ // initialization... init: function(assert){ - var A, B, C + var A, B, C, D return { // init... A: A = assert(object.C('A', { @@ -225,6 +225,7 @@ var setups = test.Setups({ return o }, test_new: function(){ + assert(this instanceof B, 'instanceof correct') assert(this.new_has_run, '.__new__() run') }, }), 'basic .__new__(..)'), // new + init... @@ -622,6 +623,33 @@ var tests = test.Tests({ var cases = test.Cases({ + 'as-is-new': function(assert){ + var A = object.C('A', { + __init__: function(){ + this.init_A_did_run = true }, + }) + var B = object.C('B', { + __new__: function(){ + return object.ASIS(A()) }, + }) + + var b = B() + + assert(b instanceof A, 'not instance of A') + assert(!(b instanceof B), 'not instance of B') + + var C = object.C('C', { + __new__: function(){ + return object.ASIS(A()) }, + __init__: function(){ + this.init_C_did_run = true }, + }) + + var c = C() + + assert(c.init_A_did_run, 'base .__init__() ran') + assert(c.init_C_did_run, 'constructor .__init__() ran') + }, 'edge-cases': function(assert){ // bad names... assert.error('Constructor(..): malformed name',