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 <alex.nanou@gmail.com>
This commit is contained in:
Alex A. Naanou 2022-12-07 15:47:24 +03:00
parent b77550f97f
commit 077eda6fb3
4 changed files with 61 additions and 4 deletions

View File

@ -130,6 +130,7 @@ class B extends A {
- [`<object>.__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.
`<context>` is the _outer_ context of the call, i.e. the object from which
`<constructor>` was referenced before it was called.
Any value returned by `.__new__(..)` will be integrated into the prototype
chain of `<object>`, if this is not desired then wrap it in
[`object.ASIS(..)`](#asis) before returning, but note that this will
not prevent `<object>.__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(..)`

View File

@ -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:

View File

@ -1,6 +1,6 @@
{
"name": "ig-object",
"version": "6.2.0",
"version": "6.2.1",
"description": "",
"main": "object.js",
"scripts": {

30
test.js
View File

@ -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',