Two divergences that cancel, discovered when fixing #9404 broke NestJS decorator metadata (PR #9465 has the story):
- A property decorator on an instance member receives the class itself as
target; the spec (and node with experimentalDecorators) hands Class.prototype.
C.constructor === C in perry; node says C.constructor === Function (a constructor object's own chain does not carry a constructor back-edge to itself).
NestJS-style code does Reflect.defineMetadata(k, v, target.constructor) — with both divergences it lands metadata on C exactly as node does with target = C.prototype, target.constructor = C. Fixing either divergence alone breaks decorator metadata; the parity suite's test_decorators_nest_common_canary and test_decorators_legacy_property_metadata are the tripwires (they caught the first attempted #9404 fix within one suite run).
The cancellation is now documented in code at the point that depends on it (the class_instance_has_member gate in resolve_proto_chain_field), so the next person who fixes one half has a pointer to the other.
Fix shape
Both halves in one change: hand decorators the reflective C.prototype object, and stop mirroring constructor as an own field answering C on the constructor side. Then run the decorator canaries plus fixtures asserting C.constructor === Function, p.constructor === C for an instance, and Reflect.getMetadata round-trips through both target and target.constructor.
Found while fixing #9404 (PR #9465).
Two divergences that cancel, discovered when fixing #9404 broke NestJS decorator metadata (PR #9465 has the story):
target; the spec (and node withexperimentalDecorators) handsClass.prototype.C.constructor === Cin perry; node saysC.constructor === Function(a constructor object's own chain does not carry aconstructorback-edge to itself).NestJS-style code does
Reflect.defineMetadata(k, v, target.constructor)— with both divergences it lands metadata onCexactly as node does withtarget = C.prototype, target.constructor = C. Fixing either divergence alone breaks decorator metadata; the parity suite'stest_decorators_nest_common_canaryandtest_decorators_legacy_property_metadataare the tripwires (they caught the first attempted #9404 fix within one suite run).The cancellation is now documented in code at the point that depends on it (the
class_instance_has_membergate inresolve_proto_chain_field), so the next person who fixes one half has a pointer to the other.Fix shape
Both halves in one change: hand decorators the reflective
C.prototypeobject, and stop mirroringconstructoras an own field answeringCon the constructor side. Then run the decorator canaries plus fixtures assertingC.constructor === Function,p.constructor === Cfor an instance, andReflect.getMetadataround-trips through bothtargetandtarget.constructor.Found while fixing #9404 (PR #9465).