diff --git a/packages/core/src/2d/sprite/MaskRenderable.ts b/packages/core/src/2d/sprite/MaskRenderable.ts index c002d4bf4b..29a3a41a0b 100644 --- a/packages/core/src/2d/sprite/MaskRenderable.ts +++ b/packages/core/src/2d/sprite/MaskRenderable.ts @@ -330,7 +330,6 @@ export function MaskRenderable( } /** @internal */ - @ignoreClone _onSpriteChange(type: SpriteModifyFlags): void { switch (type) { case SpriteModifyFlags.texture: diff --git a/packages/core/src/2d/sprite/Sprite.ts b/packages/core/src/2d/sprite/Sprite.ts index 3021521f17..28e9cb0b65 100644 --- a/packages/core/src/2d/sprite/Sprite.ts +++ b/packages/core/src/2d/sprite/Sprite.ts @@ -2,7 +2,6 @@ import { BoundingBox, MathUtil, Rect, Vector2, Vector4 } from "@galacean/engine- import { Engine } from "../../Engine"; import { UpdateFlagManager } from "../../UpdateFlagManager"; import { ReferResource } from "../../asset/ReferResource"; -import { ignoreClone } from "../../clone/CloneDecorators"; import { Texture2D } from "../../texture/Texture2D"; import { SpriteAtlas } from "../atlas/SpriteAtlas"; import { SpriteModifyFlags } from "../enums/SpriteModifyFlags"; @@ -382,7 +381,6 @@ export class Sprite extends ReferResource { this._updateFlagManager.dispatch(type); } - @ignoreClone private _onRegionChange(): void { const { _region: region } = this; // @ts-ignore @@ -398,12 +396,10 @@ export class Sprite extends ReferResource { region._onValueChanged = this._onRegionChange; } - @ignoreClone private _onPivotChange(): void { this._dispatchSpriteChange(SpriteModifyFlags.pivot); } - @ignoreClone private _onBorderChange(): void { const { _border: border } = this; // @ts-ignore diff --git a/packages/core/src/2d/sprite/SpriteRenderer.ts b/packages/core/src/2d/sprite/SpriteRenderer.ts index 6e7ec1fb17..9ba8d1f58e 100644 --- a/packages/core/src/2d/sprite/SpriteRenderer.ts +++ b/packages/core/src/2d/sprite/SpriteRenderer.ts @@ -485,7 +485,6 @@ export class SpriteRenderer extends Renderer implements ISpriteRenderer { this._dirtyUpdateFlag &= ~SpriteRendererUpdateFlags.AutomaticSize; } - @ignoreClone private _onSpriteChange(type: SpriteModifyFlags): void { switch (type) { case SpriteModifyFlags.texture: @@ -547,7 +546,6 @@ export class SpriteRenderer extends Renderer implements ISpriteRenderer { } } - @ignoreClone private _onColorChanged(): void { this._dirtyUpdateFlag |= SpriteRendererUpdateFlags.Color; } diff --git a/packages/core/src/2d/text/TextRenderer.ts b/packages/core/src/2d/text/TextRenderer.ts index 842866b3de..27771491a8 100644 --- a/packages/core/src/2d/text/TextRenderer.ts +++ b/packages/core/src/2d/text/TextRenderer.ts @@ -725,7 +725,6 @@ export class TextRenderer extends Renderer implements ITextRenderer { charRenderInfos.length = 0; } - @ignoreClone protected override _onTransformChanged(bit: TransformModifyFlags): void { super._onTransformChanged(bit); this._setDirtyFlagTrue(DirtyFlag.WorldPosition | DirtyFlag.WorldBounds); @@ -797,12 +796,10 @@ export class TextRenderer extends Renderer implements ITextRenderer { textChunks.length = 0; } - @ignoreClone private _onColorChanged(): void { this._setDirtyFlagTrue(DirtyFlag.Color); } - @ignoreClone private _onOutlineColorChanged(): void { this.shaderData.setColor(TextRenderer._outlineColorProperty, this._outlineColor); } diff --git a/packages/core/src/Camera.ts b/packages/core/src/Camera.ts index a8f6e28530..570e174bf5 100644 --- a/packages/core/src/Camera.ts +++ b/packages/core/src/Camera.ts @@ -940,7 +940,6 @@ export class Camera extends Component { return this._inverseProjectionMatrix; } - @ignoreClone private _onPixelViewportChanged(): void { this._updatePixelViewport(); this._customAspectRatio ?? this._projectionMatrixChange(); diff --git a/packages/core/src/Renderer.ts b/packages/core/src/Renderer.ts index 85525782d9..0456c35bc7 100644 --- a/packages/core/src/Renderer.ts +++ b/packages/core/src/Renderer.ts @@ -491,7 +491,6 @@ export class Renderer extends Component implements ICloneHook { } } - @ignoreClone protected _onTransformChanged(type: TransformModifyFlags): void { this._dirtyUpdateFlag |= RendererUpdateFlags.WorldVolume; } diff --git a/packages/core/src/Transform.ts b/packages/core/src/Transform.ts index a9cfec7ab1..f81e2dea7e 100644 --- a/packages/core/src/Transform.ts +++ b/packages/core/src/Transform.ts @@ -659,13 +659,11 @@ export class Transform extends Component implements ICloneHook { return parentCache; } - @ignoreClone protected _onPositionChanged(): void { this._setDirtyFlagTrue(TransformModifyFlags.LocalMatrix); this._updateWorldPositionFlag(); } - @ignoreClone protected _onWorldPositionChanged(): void { const worldPosition = this._worldPosition; const parent = this._getParentTransform(); @@ -821,14 +819,12 @@ export class Transform extends Component implements ICloneHook { this._rotateByQuat(rotQuat, relativeToLocal); } - @ignoreClone private _onRotationChanged(): void { this._setDirtyFlagTrue(TransformModifyFlags.LocalMatrix | TransformModifyFlags.LocalQuat); this._setDirtyFlagFalse(TransformModifyFlags.LocalEuler); this._updateWorldRotationFlag(); } - @ignoreClone private _onWorldRotationChanged(): void { const worldRotation = this._worldRotation; Quaternion.rotationEuler( @@ -840,14 +836,12 @@ export class Transform extends Component implements ICloneHook { this._setDirtyFlagFalse(TransformModifyFlags.WorldEuler); } - @ignoreClone private _onRotationQuaternionChanged(): void { this._setDirtyFlagTrue(TransformModifyFlags.LocalMatrix | TransformModifyFlags.LocalEuler); this._setDirtyFlagFalse(TransformModifyFlags.LocalQuat); this._updateWorldRotationFlag(); } - @ignoreClone private _onWorldRotationQuaternionChanged(): void { const worldRotationQuaternion = this._worldRotationQuaternion; const parent = this._getParentTransform(); @@ -861,7 +855,6 @@ export class Transform extends Component implements ICloneHook { this._setDirtyFlagFalse(TransformModifyFlags.WorldQuat); } - @ignoreClone private _onScaleChanged(): void { const { x, y, z } = this._scale; this._setDirtyFlagTrue(TransformModifyFlags.LocalMatrix); diff --git a/packages/core/src/audio/AudioSource.ts b/packages/core/src/audio/AudioSource.ts index 0157236a69..bd9b26d05e 100644 --- a/packages/core/src/audio/AudioSource.ts +++ b/packages/core/src/audio/AudioSource.ts @@ -238,7 +238,6 @@ export class AudioSource extends Component { this.clip = null; } - @ignoreClone private _onPlayEnd(): void { this.stop(); } diff --git a/packages/core/src/clone/CloneDecorators.ts b/packages/core/src/clone/CloneDecorators.ts index 71c88c3e13..41e0fd2938 100644 --- a/packages/core/src/clone/CloneDecorators.ts +++ b/packages/core/src/clone/CloneDecorators.ts @@ -1,7 +1,13 @@ /** @internal */ export const defaultCloneModeKey = Symbol("defaultCloneMode"); -/** @internal */ -export const fieldCloneModesKey = Symbol("fieldCloneModes"); +const fieldCloneModesKey = Symbol("fieldCloneModes"); +const symbolConstructor = Symbol as SymbolConstructor & { readonly metadata?: symbol }; +const decoratorMetadataKey = symbolConstructor.metadata ?? Symbol.for("Symbol.metadata"); +const resolvedFieldCloneModes = new WeakMap> | null>(); + +if (!symbolConstructor.metadata) { + Object.defineProperty(symbolConstructor, "metadata", { value: decoratorMetadataKey }); +} /** * @internal @@ -14,30 +20,61 @@ export const enum CloneMode { Deep } +function registerDecoratorFieldMode( + context: ClassFieldDecoratorContext, + mode: CloneMode +): void { + if (context.kind !== "field" || context.static || context.private || typeof context.name !== "string") { + throw new TypeError("Clone decorators only support public instance fields with string keys."); + } + + const metadata = context.metadata; + if (!metadata) { + throw new TypeError("Clone decorators require standard decorator metadata support."); + } + + if (!Object.prototype.hasOwnProperty.call(metadata, fieldCloneModesKey)) { + const inheritedFieldModes = metadata[fieldCloneModesKey] as Record | undefined; + Object.defineProperty(metadata, fieldCloneModesKey, { + value: Object.create(inheritedFieldModes ?? null) + }); + } + (>metadata[fieldCloneModesKey])[context.name] = mode; +} + /** - * Property decorator — deep clone this field's whole subtree, overriding the value type's default + * Field decorator — deep clone this field's whole subtree, overriding the value type's default * clone mode (field-level decorators have the highest priority). The deep intent carries into * field-cloneable members; engine-bound and platform objects keep their defaults. A decorator is * an explicit intent: if the decorated value itself can't be deep cloned (an entity reference, * asset, function, or object with opaque internal state), cloning throws rather than falling back. * Field-cloned classes reproduce only their own enumerable string-keyed properties. */ -export function deepClone(target: object, propertyKey: string): void { - CloneMetadata.registerFieldMode(target, propertyKey, CloneMode.Deep); +export function deepClone( + _value: undefined, + context: ClassFieldDecoratorContext +): void { + registerDecoratorFieldMode(context, CloneMode.Deep); } /** - * Property decorator — assign (share the reference) this field, overriding the value type's default clone mode. + * Field decorator — assign (share the reference) this field, overriding the value type's default clone mode. */ -export function assignmentClone(target: object, propertyKey: string): void { - CloneMetadata.registerFieldMode(target, propertyKey, CloneMode.Assignment); +export function assignmentClone( + _value: undefined, + context: ClassFieldDecoratorContext +): void { + registerDecoratorFieldMode(context, CloneMode.Assignment); } /** - * Property decorator — ignore this field when cloning; keep the clone's own constructor-built value. + * Field decorator — ignore this field when cloning; keep the clone's own constructor-built value. */ -export function ignoreClone(target: object, propertyKey: string): void { - CloneMetadata.registerFieldMode(target, propertyKey, CloneMode.Ignore); +export function ignoreClone( + _value: undefined, + context: ClassFieldDecoratorContext +): void { + registerDecoratorFieldMode(context, CloneMode.Ignore); } /** @@ -47,14 +84,38 @@ export function registerDefaultCloneMode(target: { prototype: object }, mode: Cl Object.defineProperty(target.prototype, defaultCloneModeKey, { value: mode }); } -class CloneMetadata { - static registerFieldMode(target: any, propertyKey: string, mode: CloneMode): void { - if (!Object.prototype.hasOwnProperty.call(target, fieldCloneModesKey)) { - Object.defineProperty(target, fieldCloneModesKey, { - value: Object.create(target[fieldCloneModesKey] ?? null), - configurable: true - }); - } - target[fieldCloneModesKey][propertyKey] = mode; +/** + * @internal + */ +export function getFieldCloneModes(target: object): Readonly> | undefined { + const constructor = Object.getPrototypeOf(target)?.constructor as + | (Function & Record>) + | undefined; + return constructor ? resolveFieldCloneModes(constructor) : undefined; +} + +function resolveFieldCloneModes( + constructor: Function & Record> +): Readonly> | undefined { + const cached = resolvedFieldCloneModes.get(constructor); + if (cached !== undefined) { + return cached ?? undefined; } + + const parentConstructor = Object.getPrototypeOf(constructor); + const parentModes = + typeof parentConstructor === "function" && parentConstructor !== Function.prototype + ? resolveFieldCloneModes(parentConstructor) + : undefined; + const metadata = Object.prototype.hasOwnProperty.call(constructor, decoratorMetadataKey) + ? constructor[decoratorMetadataKey] + : undefined; + const ownModes = + metadata && Object.prototype.hasOwnProperty.call(metadata, fieldCloneModesKey) + ? (metadata[fieldCloneModesKey] as Record) + : undefined; + const resolvedModes = ownModes ? Object.assign(Object.create(parentModes ?? null), ownModes) : parentModes; + + resolvedFieldCloneModes.set(constructor, resolvedModes ?? null); + return resolvedModes; } diff --git a/packages/core/src/clone/CloneUtil.ts b/packages/core/src/clone/CloneUtil.ts index 2ab1b76942..ad86648234 100644 --- a/packages/core/src/clone/CloneUtil.ts +++ b/packages/core/src/clone/CloneUtil.ts @@ -22,7 +22,7 @@ import { Component } from "../Component"; import { Entity } from "../Entity"; import { UpdateFlag } from "../UpdateFlag"; import { UpdateFlagManager } from "../UpdateFlagManager"; -import { CloneMode, defaultCloneModeKey, fieldCloneModesKey, registerDefaultCloneMode } from "./CloneDecorators"; +import { CloneMode, defaultCloneModeKey, getFieldCloneModes, registerDefaultCloneMode } from "./CloneDecorators"; import type { ICloneHook } from "./ICloneHook"; /** @@ -38,7 +38,7 @@ export class CloneUtil { cloneMap: Map, deepCloneSubtree = false ): void { - const fieldModes = source[fieldCloneModesKey]; + const fieldModes = getFieldCloneModes(source); const keys = Object.keys(source); for (let i = 0, n = keys.length; i < n; i++) { const k = keys[i]; diff --git a/packages/core/src/clone/ComponentCloner.ts b/packages/core/src/clone/ComponentCloner.ts index ac646f192a..9e202504cd 100644 --- a/packages/core/src/clone/ComponentCloner.ts +++ b/packages/core/src/clone/ComponentCloner.ts @@ -1,6 +1,6 @@ import { Component } from "../Component"; import { CloneUtil } from "./CloneUtil"; -import { CloneMode, fieldCloneModesKey } from "./CloneDecorators"; +import { CloneMode, getFieldCloneModes } from "./CloneDecorators"; import type { ICloneHook } from "./ICloneHook"; /** @@ -12,7 +12,7 @@ export class ComponentCloner { * @internal */ static cloneComponent(source: Component, target: Component, cloneMap: Map): void { - const fieldModes = (source)[fieldCloneModesKey]; + const fieldModes = getFieldCloneModes(source); const keys = Object.keys(source); for (let i = 0, n = keys.length; i < n; i++) { const k = keys[i]; diff --git a/packages/core/src/mesh/MeshRenderer.ts b/packages/core/src/mesh/MeshRenderer.ts index 0c1c23a572..7566b9e8b3 100644 --- a/packages/core/src/mesh/MeshRenderer.ts +++ b/packages/core/src/mesh/MeshRenderer.ts @@ -216,7 +216,6 @@ export class MeshRenderer extends Renderer { this._mesh = mesh; } - @ignoreClone private _onMeshChanged(type: MeshModifyFlags): void { type & MeshModifyFlags.Bounds && (this._dirtyUpdateFlag |= RendererUpdateFlags.WorldVolume); type & MeshModifyFlags.VertexElements && (this._dirtyUpdateFlag |= MeshRendererUpdateFlags.VertexElementMacro); diff --git a/packages/core/src/mesh/SkinnedMeshRenderer.ts b/packages/core/src/mesh/SkinnedMeshRenderer.ts index 0077a8aa3d..855a0fa0a6 100644 --- a/packages/core/src/mesh/SkinnedMeshRenderer.ts +++ b/packages/core/src/mesh/SkinnedMeshRenderer.ts @@ -247,12 +247,10 @@ export class SkinnedMeshRenderer extends MeshRenderer { } } - @ignoreClone private _onLocalBoundsChanged(): void { this._dirtyUpdateFlag |= RendererUpdateFlags.WorldVolume; } - @ignoreClone private _onSkinUpdated(type: SkinUpdateFlag, value: number | Entity): void { switch (type) { case SkinUpdateFlag.BoneCountChanged: diff --git a/packages/core/src/particle/ParticleRenderer.ts b/packages/core/src/particle/ParticleRenderer.ts index f2e23a31f5..b117c06da9 100644 --- a/packages/core/src/particle/ParticleRenderer.ts +++ b/packages/core/src/particle/ParticleRenderer.ts @@ -300,7 +300,6 @@ export class ParticleRenderer extends Renderer { /** * @internal */ - @ignoreClone _onGeneratorParamsChanged(): void { this._dirtyUpdateFlag |= ParticleUpdateFlags.GeneratorVolume | ParticleUpdateFlags.TransformVolume | RendererUpdateFlags.WorldVolume; @@ -309,7 +308,6 @@ export class ParticleRenderer extends Renderer { /** * @internal */ - @ignoreClone override _onTransformChanged(type: TransformModifyFlags): void { this._dirtyUpdateFlag |= ParticleUpdateFlags.TransformVolume | RendererUpdateFlags.WorldVolume; } diff --git a/packages/core/src/particle/modules/ParticleGradient.ts b/packages/core/src/particle/modules/ParticleGradient.ts index 0dd2902a3f..960f818a75 100644 --- a/packages/core/src/particle/modules/ParticleGradient.ts +++ b/packages/core/src/particle/modules/ParticleGradient.ts @@ -272,12 +272,10 @@ export class ParticleGradient extends DataObject { keys.splice(index, 1); } - @ignoreClone private _setColorTypeArrayDirty(): void { this._colorTypeArrayDirty = true; } - @ignoreClone private _setAlphaTypeArrayDirty(): void { this._alphaTypeArrayDirty = true; } diff --git a/packages/core/src/particle/modules/TextureSheetAnimationModule.ts b/packages/core/src/particle/modules/TextureSheetAnimationModule.ts index 868e78bfa1..489e8597c2 100644 --- a/packages/core/src/particle/modules/TextureSheetAnimationModule.ts +++ b/packages/core/src/particle/modules/TextureSheetAnimationModule.ts @@ -89,7 +89,6 @@ export class TextureSheetAnimationModule extends ParticleGeneratorModule { this._frameOverTimeRand.reset(randomSeed, ParticleRandomSubSeeds.TextureSheetAnimation); } - @ignoreClone private _onTilingChanged(): void { const tiling = this.tiling; this._tillingInfo.set(1.0 / tiling.x, 1.0 / tiling.y, tiling.x * tiling.y); diff --git a/packages/core/src/particle/modules/shape/MeshShape.ts b/packages/core/src/particle/modules/shape/MeshShape.ts index 7fd19e7b68..a092a7d068 100644 --- a/packages/core/src/particle/modules/shape/MeshShape.ts +++ b/packages/core/src/particle/modules/shape/MeshShape.ts @@ -140,7 +140,6 @@ export class MeshShape extends BaseShape implements ICloneHook { return typedBuffer; } - @ignoreClone private _onMeshChanged(type: MeshModifyFlags): void { if (type & MeshModifyFlags.VertexElements) { const mesh = this._mesh; diff --git a/packages/core/src/physics/CharacterController.ts b/packages/core/src/physics/CharacterController.ts index 76c955d961..45701b8484 100644 --- a/packages/core/src/physics/CharacterController.ts +++ b/packages/core/src/physics/CharacterController.ts @@ -5,7 +5,6 @@ import { Entity } from "../Entity"; import { Collider } from "./Collider"; import { ControllerNonWalkableMode } from "./enums/ControllerNonWalkableMode"; import { ColliderShape } from "./shape"; -import { ignoreClone } from "../clone/CloneDecorators"; /** * The character controllers. @@ -169,7 +168,6 @@ export class CharacterController extends Collider { (this._nativeCollider).getWorldPosition(this.entity.transform.worldPosition); } - @ignoreClone private _setUpDirection(): void { (this._nativeCollider).setUpDirection(this._upDirection); } diff --git a/packages/core/src/physics/DynamicCollider.ts b/packages/core/src/physics/DynamicCollider.ts index d69ecb3d1f..4c1ab6f228 100644 --- a/packages/core/src/physics/DynamicCollider.ts +++ b/packages/core/src/physics/DynamicCollider.ts @@ -566,17 +566,14 @@ export class DynamicCollider extends Collider { this._automaticInertiaTensor || (this._nativeCollider).setInertiaTensor(this._inertiaTensor); } - @ignoreClone private _setLinearVelocity(): void { (this._nativeCollider).setLinearVelocity(this._linearVelocity); } - @ignoreClone private _setAngularVelocity(): void { (this._nativeCollider).setAngularVelocity(this._angularVelocity); } - @ignoreClone private _handleCenterOfMassChanged(): void { if (this._automaticCenterOfMass) { console.warn( @@ -587,7 +584,6 @@ export class DynamicCollider extends Collider { } } - @ignoreClone private _handleInertiaTensorChanged(): void { if (this._automaticInertiaTensor) { console.warn( diff --git a/packages/core/src/physics/joint/HingeJoint.ts b/packages/core/src/physics/joint/HingeJoint.ts index bb46e1c9fe..ddd6c80006 100644 --- a/packages/core/src/physics/joint/HingeJoint.ts +++ b/packages/core/src/physics/joint/HingeJoint.ts @@ -6,7 +6,6 @@ import { HingeJointFlag } from "../enums/HingeJointFlag"; import { Joint } from "./Joint"; import { JointLimits } from "./JointLimits"; import { JointMotor } from "./JointMotor"; -import { ignoreClone } from "../../clone/CloneDecorators"; import { Entity } from "../../Entity"; /** @@ -174,7 +173,6 @@ export class HingeJoint extends Joint { } } - @ignoreClone private _onMotorChanged() { const motor = this._jointMotor; if (this._nativeJoint) { @@ -185,7 +183,6 @@ export class HingeJoint extends Joint { } } - @ignoreClone private _onLimitsChanged() { const limits = this._limits; if (limits && this._nativeJoint) { @@ -197,7 +194,6 @@ export class HingeJoint extends Joint { } } - @ignoreClone private _onAxisChanged(): void { //@ts-ignore this._axis._onValueChanged = null; diff --git a/packages/core/src/physics/joint/Joint.ts b/packages/core/src/physics/joint/Joint.ts index 5b60236a2d..18cb3759fd 100644 --- a/packages/core/src/physics/joint/Joint.ts +++ b/packages/core/src/physics/joint/Joint.ts @@ -253,7 +253,6 @@ export abstract class Joint extends Component { this._updateActualAnchor(AnchorOwner.Connected); } - @ignoreClone private _handleConnectedAnchorChanged(): void { if (this._automaticConnectedAnchor) { console.warn("Cannot set connectedAnchor when automaticConnectedAnchor is true."); @@ -262,14 +261,12 @@ export abstract class Joint extends Component { } } - @ignoreClone private _onSelfTransformChanged(type: TransformModifyFlags): void { if (type & TransformModifyFlags.WorldScale) { this._updateActualAnchor(AnchorOwner.Self); } } - @ignoreClone private _onConnectedTransformChanged(type: TransformModifyFlags): void { if (type & TransformModifyFlags.WorldScale) { this._updateActualAnchor(AnchorOwner.Connected); diff --git a/packages/core/src/physics/shape/BoxColliderShape.ts b/packages/core/src/physics/shape/BoxColliderShape.ts index 32dc75d1c0..f5e7197458 100644 --- a/packages/core/src/physics/shape/BoxColliderShape.ts +++ b/packages/core/src/physics/shape/BoxColliderShape.ts @@ -2,7 +2,6 @@ import { ColliderShape } from "./ColliderShape"; import { IBoxColliderShape } from "@galacean/engine-design"; import { Vector3 } from "@galacean/engine-math"; import { Engine } from "../../Engine"; -import { ignoreClone } from "../../clone/CloneDecorators"; /** * Physical collider shape for box. @@ -40,7 +39,6 @@ export class BoxColliderShape extends ColliderShape { this._setSize(); } - @ignoreClone private _setSize(): void { (this._nativeShape).setSize(this._size); } diff --git a/packages/core/src/physics/shape/ColliderShape.ts b/packages/core/src/physics/shape/ColliderShape.ts index a727f803bc..c1dc6f4e1e 100644 --- a/packages/core/src/physics/shape/ColliderShape.ts +++ b/packages/core/src/physics/shape/ColliderShape.ts @@ -196,13 +196,11 @@ export abstract class ColliderShape extends DataObject implements ICloneHook(ASTNode: T) { - ASTNode.prototype.nt = nonTerminal; - (ASTNode).pool = ShaderCompilerUtils.createObjectPool(ASTNode); - }; -} - export abstract class TreeNode implements IPoolElement { - static pool: ClearableObjectPool void }>; + private static _pools = new WeakMap>(); /** The non-terminal in grammar. */ - nt: NoneTerminal; + get nt(): NoneTerminal { + return (this.constructor).nonTerminal; + } + + static get pool(): ClearableObjectPool { + let pool = TreeNode._pools.get(this); + if (!pool) { + pool = ShaderCompilerUtils.createObjectPool( TreeNode>(this)); + TreeNode._pools.set(this, pool); + } + return pool; + } + private _children: NodeChild[]; private _parent: TreeNode; private _location: ShaderRange; @@ -116,25 +121,26 @@ export namespace ASTNode { sa.semanticStack.push(node); } - @ASTNodeDecorator(NoneTerminal._ignore) - export class TrivialNode extends TreeNode {} + export class TrivialNode extends TreeNode { + static readonly nonTerminal = NoneTerminal._ignore; + } - @ASTNodeDecorator(NoneTerminal.scope_brace) export class ScopeBrace extends TreeNode { + static readonly nonTerminal = NoneTerminal.scope_brace; override semanticAnalyze(sa: SemanticAnalyzer): void { sa.pushScope(); } } - @ASTNodeDecorator(NoneTerminal.scope_end_brace) export class ScopeEndBrace extends TreeNode { + static readonly nonTerminal = NoneTerminal.scope_end_brace; override semanticAnalyze(sa: SemanticAnalyzer): void { sa.popScope(); } } - @ASTNodeDecorator(NoneTerminal.jump_statement) export class JumpStatement extends TreeNode { + static readonly nonTerminal = NoneTerminal.jump_statement; isFragReturnStatement: boolean; override init(): void { @@ -153,26 +159,33 @@ export namespace ASTNode { } // #if _VERBOSE - @ASTNodeDecorator(NoneTerminal.conditionopt) - export class ConditionOpt extends TreeNode {} + export class ConditionOpt extends TreeNode { + static readonly nonTerminal = NoneTerminal.conditionopt; + } - @ASTNodeDecorator(NoneTerminal.for_rest_statement) - export class ForRestStatement extends TreeNode {} + export class ForRestStatement extends TreeNode { + static readonly nonTerminal = NoneTerminal.for_rest_statement; + } - @ASTNodeDecorator(NoneTerminal.condition) - export class Condition extends TreeNode {} + export class Condition extends TreeNode { + static readonly nonTerminal = NoneTerminal.condition; + } - @ASTNodeDecorator(NoneTerminal.for_init_statement) - export class ForInitStatement extends TreeNode {} + export class ForInitStatement extends TreeNode { + static readonly nonTerminal = NoneTerminal.for_init_statement; + } - @ASTNodeDecorator(NoneTerminal.iteration_statement) - export class IterationStatement extends TreeNode {} + export class IterationStatement extends TreeNode { + static readonly nonTerminal = NoneTerminal.iteration_statement; + } - @ASTNodeDecorator(NoneTerminal.selection_statement) - export class SelectionStatement extends TreeNode {} + export class SelectionStatement extends TreeNode { + static readonly nonTerminal = NoneTerminal.selection_statement; + } - @ASTNodeDecorator(NoneTerminal.expression_statement) - export class ExpressionStatement extends TreeNode {} + export class ExpressionStatement extends TreeNode { + static readonly nonTerminal = NoneTerminal.expression_statement; + } // #endif export abstract class ExpressionAstNode extends TreeNode { @@ -190,16 +203,16 @@ export namespace ASTNode { } // #if _VERBOSE - @ASTNodeDecorator(NoneTerminal.initializer_list) export class InitializerList extends ExpressionAstNode { + static readonly nonTerminal = NoneTerminal.initializer_list; override semanticAnalyze(sa: SemanticAnalyzer): void { const init = this.children[0] as Initializer | InitializerList; this.type = init.type; } } - @ASTNodeDecorator(NoneTerminal.initializer) export class Initializer extends ExpressionAstNode { + static readonly nonTerminal = NoneTerminal.initializer; override semanticAnalyze(sa: SemanticAnalyzer): void { if (this.children.length === 1) { this.type = (this.children[0]).type; @@ -210,8 +223,8 @@ export namespace ASTNode { } // #endif - @ASTNodeDecorator(NoneTerminal.single_declaration) export class SingleDeclaration extends TreeNode { + static readonly nonTerminal = NoneTerminal.single_declaration; typeSpecifier: TypeSpecifier; arraySpecifier?: ArraySpecifier; @@ -257,8 +270,8 @@ export namespace ASTNode { } } - @ASTNodeDecorator(NoneTerminal.fully_specified_type) export class FullySpecifiedType extends TreeNode { + static readonly nonTerminal = NoneTerminal.fully_specified_type; typeSpecifier: TypeSpecifier; type: GalaceanDataType; @@ -269,11 +282,12 @@ export namespace ASTNode { } } - @ASTNodeDecorator(NoneTerminal.type_qualifier) - export class TypeQualifier extends TreeNode {} + export class TypeQualifier extends TreeNode { + static readonly nonTerminal = NoneTerminal.type_qualifier; + } - @ASTNodeDecorator(NoneTerminal.single_type_qualifier) export class SingleTypeQualifier extends TreeNode { + static readonly nonTerminal = NoneTerminal.single_type_qualifier; qualifier: Keyword; lexeme: string; @@ -301,21 +315,25 @@ export namespace ASTNode { } // #if _VERBOSE - @ASTNodeDecorator(NoneTerminal.storage_qualifier) - export class StorageQualifier extends BasicTypeQualifier {} + export class StorageQualifier extends BasicTypeQualifier { + static readonly nonTerminal = NoneTerminal.storage_qualifier; + } - @ASTNodeDecorator(NoneTerminal.precision_qualifier) - export class PrecisionQualifier extends BasicTypeQualifier {} + export class PrecisionQualifier extends BasicTypeQualifier { + static readonly nonTerminal = NoneTerminal.precision_qualifier; + } - @ASTNodeDecorator(NoneTerminal.interpolation_qualifier) - export class InterpolationQualifier extends BasicTypeQualifier {} + export class InterpolationQualifier extends BasicTypeQualifier { + static readonly nonTerminal = NoneTerminal.interpolation_qualifier; + } - @ASTNodeDecorator(NoneTerminal.invariant_qualifier) - export class InvariantQualifier extends BasicTypeQualifier {} + export class InvariantQualifier extends BasicTypeQualifier { + static readonly nonTerminal = NoneTerminal.invariant_qualifier; + } // #endif - @ASTNodeDecorator(NoneTerminal.type_specifier) export class TypeSpecifier extends TreeNode { + static readonly nonTerminal = NoneTerminal.type_specifier; type: GalaceanDataType; lexeme: string; arraySize?: number; @@ -338,8 +356,8 @@ export namespace ASTNode { } } - @ASTNodeDecorator(NoneTerminal.array_specifier) export class ArraySpecifier extends TreeNode { + static readonly nonTerminal = NoneTerminal.array_specifier; size: number | undefined; override semanticAnalyze(sa: SemanticAnalyzer): void { const integerConstantExpr = this.children[1] as IntegerConstantExpression; @@ -347,8 +365,8 @@ export namespace ASTNode { } } - @ASTNodeDecorator(NoneTerminal.integer_constant_expression_operator) export class IntegerConstantExpressionOperator extends TreeNode { + static readonly nonTerminal = NoneTerminal.integer_constant_expression_operator; compute: (a: number, b: number) => number; lexeme: string; @@ -377,8 +395,8 @@ export namespace ASTNode { } } - @ASTNodeDecorator(NoneTerminal.integer_constant_expression) export class IntegerConstantExpression extends TreeNode { + static readonly nonTerminal = NoneTerminal.integer_constant_expression; value?: number; override init(): void { @@ -404,8 +422,8 @@ export namespace ASTNode { } } - @ASTNodeDecorator(NoneTerminal.type_specifier_nonarray) export class TypeSpecifierNonArray extends TreeNode { + static readonly nonTerminal = NoneTerminal.type_specifier_nonarray; type: GalaceanDataType; lexeme: string; @@ -426,8 +444,8 @@ export namespace ASTNode { } } - @ASTNodeDecorator(NoneTerminal.ext_builtin_type_specifier_nonarray) export class ExtBuiltinTypeSpecifierNonArray extends TreeNode { + static readonly nonTerminal = NoneTerminal.ext_builtin_type_specifier_nonarray; type: TokenType; lexeme: string; @@ -438,8 +456,8 @@ export namespace ASTNode { } } - @ASTNodeDecorator(NoneTerminal.init_declarator_list) export class InitDeclaratorList extends TreeNode { + static readonly nonTerminal = NoneTerminal.init_declarator_list; typeInfo: SymbolType; override semanticAnalyze(sa: SemanticAnalyzer): void { @@ -474,8 +492,8 @@ export namespace ASTNode { } } - @ASTNodeDecorator(NoneTerminal.identifier_list) export class IdentifierList extends TreeNode { + static readonly nonTerminal = NoneTerminal.identifier_list; idList: BaseToken[] = []; override init(): void { @@ -500,15 +518,15 @@ export namespace ASTNode { } } - @ASTNodeDecorator(NoneTerminal.declaration) export class Declaration extends TreeNode { + static readonly nonTerminal = NoneTerminal.declaration; override codeGen(visitor: CodeGenVisitor): string { return this.setCache(visitor.visitDeclaration(this)); } } - @ASTNodeDecorator(NoneTerminal.function_prototype) export class FunctionProtoType extends TreeNode { + static readonly nonTerminal = NoneTerminal.function_prototype; ident: BaseToken; returnType: FullySpecifiedType; parameterList: IParamInfo[]; @@ -523,8 +541,8 @@ export namespace ASTNode { } } - @ASTNodeDecorator(NoneTerminal.function_declarator) export class FunctionDeclarator extends TreeNode { + static readonly nonTerminal = NoneTerminal.function_declarator; ident: BaseToken; returnType: FullySpecifiedType; parameterInfoList: IParamInfo[] | undefined; @@ -544,8 +562,8 @@ export namespace ASTNode { } } - @ASTNodeDecorator(NoneTerminal.function_header) export class FunctionHeader extends TreeNode { + static readonly nonTerminal = NoneTerminal.function_header; ident: BaseToken; returnType: FullySpecifiedType; @@ -561,8 +579,8 @@ export namespace ASTNode { } } - @ASTNodeDecorator(NoneTerminal.function_parameter_list) export class FunctionParameterList extends TreeNode { + static readonly nonTerminal = NoneTerminal.function_parameter_list; parameterInfoList: IParamInfo[] = []; paramSig: GalaceanDataType[] = []; @@ -603,17 +621,20 @@ export namespace ASTNode { } } - @ASTNodeDecorator(NoneTerminal.macro_param_case_list) - export class MacroParamCaseList extends TreeNode {} + export class MacroParamCaseList extends TreeNode { + static readonly nonTerminal = NoneTerminal.macro_param_case_list; + } - @ASTNodeDecorator(NoneTerminal.macro_param_block) - export class MacroParamBlock extends TreeNode {} + export class MacroParamBlock extends TreeNode { + static readonly nonTerminal = NoneTerminal.macro_param_block; + } - @ASTNodeDecorator(NoneTerminal.macro_parameter_branch) - export class MacroParameterBranch extends TreeNode {} + export class MacroParameterBranch extends TreeNode { + static readonly nonTerminal = NoneTerminal.macro_parameter_branch; + } - @ASTNodeDecorator(NoneTerminal.parameter_declaration) export class ParameterDeclaration extends TreeNode { + static readonly nonTerminal = NoneTerminal.parameter_declaration; // Some syntax is not recognized, eg. // `#define TEXTURE2D_SHADOW_PARAM(shadowMap) mediump sampler2D shadowMap` typeInfo?: SymbolType; @@ -648,8 +669,8 @@ export namespace ASTNode { } } - @ASTNodeDecorator(NoneTerminal.parameter_declarator) export class ParameterDeclarator extends TreeNode { + static readonly nonTerminal = NoneTerminal.parameter_declarator; ident: BaseToken; typeInfo: SymbolType; @@ -663,30 +684,34 @@ export namespace ASTNode { } // #if _VERBOSE - @ASTNodeDecorator(NoneTerminal.simple_statement) - export class SimpleStatement extends TreeNode {} + export class SimpleStatement extends TreeNode { + static readonly nonTerminal = NoneTerminal.simple_statement; + } - @ASTNodeDecorator(NoneTerminal.compound_statement) - export class CompoundStatement extends TreeNode {} + export class CompoundStatement extends TreeNode { + static readonly nonTerminal = NoneTerminal.compound_statement; + } // #endif - @ASTNodeDecorator(NoneTerminal.compound_statement_no_scope) - export class CompoundStatementNoScope extends TreeNode {} + export class CompoundStatementNoScope extends TreeNode { + static readonly nonTerminal = NoneTerminal.compound_statement_no_scope; + } // #if _VERBOSE - @ASTNodeDecorator(NoneTerminal.statement) - export class Statement extends TreeNode {} + export class Statement extends TreeNode { + static readonly nonTerminal = NoneTerminal.statement; + } // #endif - @ASTNodeDecorator(NoneTerminal.statement_list) export class StatementList extends TreeNode { + static readonly nonTerminal = NoneTerminal.statement_list; override codeGen(visitor: CodeGenVisitor): string { return this.setCache(visitor.visitStatementList(this)); } } - @ASTNodeDecorator(NoneTerminal.function_definition) export class FunctionDefinition extends TreeNode { + static readonly nonTerminal = NoneTerminal.function_definition; returnStatement?: ASTNode.JumpStatement; protoType: FunctionProtoType; statements: CompoundStatementNoScope; @@ -728,8 +753,8 @@ export namespace ASTNode { } } - @ASTNodeDecorator(NoneTerminal.function_call) export class FunctionCall extends ExpressionAstNode { + static readonly nonTerminal = NoneTerminal.function_call; override semanticAnalyze(sa: SemanticAnalyzer): void { this.type = (this.children[0] as FunctionCallGeneric).type; } @@ -739,8 +764,8 @@ export namespace ASTNode { } } - @ASTNodeDecorator(NoneTerminal.function_call_generic) export class FunctionCallGeneric extends ExpressionAstNode { + static readonly nonTerminal = NoneTerminal.function_call_generic; fnSymbol: FnSymbol | StructSymbol | undefined; override init(): void { @@ -787,8 +812,8 @@ export namespace ASTNode { } } - @ASTNodeDecorator(NoneTerminal.function_call_parameter_list) export class FunctionCallParameterList extends TreeNode { + static readonly nonTerminal = NoneTerminal.function_call_parameter_list; paramSig: GalaceanDataType[] = []; paramNodes: Array = []; @@ -818,15 +843,18 @@ export namespace ASTNode { } } - @ASTNodeDecorator(NoneTerminal.macro_call_arg_case_list) - export class MacroCallArgCaseList extends TreeNode {} - @ASTNodeDecorator(NoneTerminal.macro_call_arg_block) - export class MacroCallArgBlock extends TreeNode {} - @ASTNodeDecorator(NoneTerminal.macro_call_arg_branch) - export class MacroCallArgBranch extends TreeNode {} + export class MacroCallArgCaseList extends TreeNode { + static readonly nonTerminal = NoneTerminal.macro_call_arg_case_list; + } + export class MacroCallArgBlock extends TreeNode { + static readonly nonTerminal = NoneTerminal.macro_call_arg_block; + } + export class MacroCallArgBranch extends TreeNode { + static readonly nonTerminal = NoneTerminal.macro_call_arg_branch; + } - @ASTNodeDecorator(NoneTerminal.precision_specifier) export class PrecisionSpecifier extends TreeNode { + static readonly nonTerminal = NoneTerminal.precision_specifier; override semanticAnalyze(sa: SemanticAnalyzer): void { if (!sa.symbolTableStack.isInMacroBranch) { sa.shaderData.globalPrecisions.push(this); @@ -834,8 +862,8 @@ export namespace ASTNode { } } - @ASTNodeDecorator(NoneTerminal.function_identifier) export class FunctionIdentifier extends TreeNode { + static readonly nonTerminal = NoneTerminal.function_identifier; ident: GalaceanDataType; lexeme: string; isBuiltin: boolean; @@ -853,8 +881,8 @@ export namespace ASTNode { } } - @ASTNodeDecorator(NoneTerminal.assignment_expression) export class AssignmentExpression extends ExpressionAstNode { + static readonly nonTerminal = NoneTerminal.assignment_expression; override semanticAnalyze(sa: SemanticAnalyzer): void { if (this.children.length === 1) { const expr = this.children[0] as ConditionalExpression; @@ -867,12 +895,13 @@ export namespace ASTNode { } // #if _VERBOSE - @ASTNodeDecorator(NoneTerminal.assignment_operator) - export class AssignmentOperator extends TreeNode {} + export class AssignmentOperator extends TreeNode { + static readonly nonTerminal = NoneTerminal.assignment_operator; + } // #endif - @ASTNodeDecorator(NoneTerminal.expression) export class Expression extends ExpressionAstNode { + static readonly nonTerminal = NoneTerminal.expression; override semanticAnalyze(sa: SemanticAnalyzer): void { if (this.children.length === 1) { const expr = this.children[0] as AssignmentExpression; @@ -884,8 +913,8 @@ export namespace ASTNode { } } - @ASTNodeDecorator(NoneTerminal.primary_expression) export class PrimaryExpression extends ExpressionAstNode { + static readonly nonTerminal = NoneTerminal.primary_expression; override semanticAnalyze(sa: SemanticAnalyzer): void { if (this.children.length === 1) { const id = this.children[0]; @@ -912,8 +941,8 @@ export namespace ASTNode { } } - @ASTNodeDecorator(NoneTerminal.postfix_expression) export class PostfixExpression extends ExpressionAstNode { + static readonly nonTerminal = NoneTerminal.postfix_expression; override init(): void { super.init(); if (this.children.length === 1) { @@ -928,18 +957,19 @@ export namespace ASTNode { } // #if _VERBOSE - @ASTNodeDecorator(NoneTerminal.unary_operator) - export class UnaryOperator extends TreeNode {} + export class UnaryOperator extends TreeNode { + static readonly nonTerminal = NoneTerminal.unary_operator; + } - @ASTNodeDecorator(NoneTerminal.unary_expression) export class UnaryExpression extends ExpressionAstNode { + static readonly nonTerminal = NoneTerminal.unary_expression; override init(): void { this.type = (this.children[0] as PostfixExpression).type; } } - @ASTNodeDecorator(NoneTerminal.multiplicative_expression) export class MultiplicativeExpression extends ExpressionAstNode { + static readonly nonTerminal = NoneTerminal.multiplicative_expression; override init(): void { super.init(); if (this.children.length === 1) { @@ -955,8 +985,8 @@ export namespace ASTNode { } } - @ASTNodeDecorator(NoneTerminal.additive_expression) export class AdditiveExpression extends ExpressionAstNode { + static readonly nonTerminal = NoneTerminal.additive_expression; override init(): void { super.init(); if (this.children.length === 1) { @@ -972,16 +1002,16 @@ export namespace ASTNode { } } - @ASTNodeDecorator(NoneTerminal.shift_expression) export class ShiftExpression extends ExpressionAstNode { + static readonly nonTerminal = NoneTerminal.shift_expression; override semanticAnalyze(sa: SemanticAnalyzer): void { const expr = this.children[0] as ExpressionAstNode; this.type = expr.type; } } - @ASTNodeDecorator(NoneTerminal.relational_expression) export class RelationalExpression extends ExpressionAstNode { + static readonly nonTerminal = NoneTerminal.relational_expression; override semanticAnalyze(sa: SemanticAnalyzer): void { if (this.children.length === 1) { this.type = (this.children[0]).type; @@ -991,8 +1021,8 @@ export namespace ASTNode { } } - @ASTNodeDecorator(NoneTerminal.equality_expression) export class EqualityExpression extends ExpressionAstNode { + static readonly nonTerminal = NoneTerminal.equality_expression; override semanticAnalyze(sa: SemanticAnalyzer): void { if (this.children.length === 1) { this.type = (this.children[0]).type; @@ -1002,8 +1032,8 @@ export namespace ASTNode { } } - @ASTNodeDecorator(NoneTerminal.and_expression) export class AndExpression extends ExpressionAstNode { + static readonly nonTerminal = NoneTerminal.and_expression; override semanticAnalyze(sa: SemanticAnalyzer): void { if (this.children.length === 1) { this.type = (this.children[0]).type; @@ -1013,8 +1043,8 @@ export namespace ASTNode { } } - @ASTNodeDecorator(NoneTerminal.exclusive_or_expression) export class ExclusiveOrExpression extends ExpressionAstNode { + static readonly nonTerminal = NoneTerminal.exclusive_or_expression; override semanticAnalyze(sa: SemanticAnalyzer): void { if (this.children.length === 1) { this.type = (this.children[0]).type; @@ -1024,8 +1054,8 @@ export namespace ASTNode { } } - @ASTNodeDecorator(NoneTerminal.inclusive_or_expression) export class InclusiveOrExpression extends ExpressionAstNode { + static readonly nonTerminal = NoneTerminal.inclusive_or_expression; override semanticAnalyze(sa: SemanticAnalyzer): void { if (this.children.length === 1) { this.type = (this.children[0]).type; @@ -1035,8 +1065,8 @@ export namespace ASTNode { } } - @ASTNodeDecorator(NoneTerminal.logical_and_expression) export class LogicalAndExpression extends ExpressionAstNode { + static readonly nonTerminal = NoneTerminal.logical_and_expression; override semanticAnalyze(sa: SemanticAnalyzer): void { if (this.children.length === 1) { this.type = (this.children[0]).type; @@ -1046,8 +1076,8 @@ export namespace ASTNode { } } - @ASTNodeDecorator(NoneTerminal.logical_xor_expression) export class LogicalXorExpression extends ExpressionAstNode { + static readonly nonTerminal = NoneTerminal.logical_xor_expression; override semanticAnalyze(sa: SemanticAnalyzer): void { if (this.children.length === 1) { this.type = (this.children[0]).type; @@ -1057,8 +1087,8 @@ export namespace ASTNode { } } - @ASTNodeDecorator(NoneTerminal.logical_or_expression) export class LogicalOrExpression extends ExpressionAstNode { + static readonly nonTerminal = NoneTerminal.logical_or_expression; override semanticAnalyze(sa: SemanticAnalyzer): void { if (this.children.length === 1) { this.type = (this.children[0]).type; @@ -1068,8 +1098,8 @@ export namespace ASTNode { } } - @ASTNodeDecorator(NoneTerminal.conditional_expression) export class ConditionalExpression extends ExpressionAstNode { + static readonly nonTerminal = NoneTerminal.conditional_expression; override semanticAnalyze(sa: SemanticAnalyzer): void { if (this.children.length === 1) { this.type = (this.children[0]).type; @@ -1078,8 +1108,8 @@ export namespace ASTNode { } // #endif - @ASTNodeDecorator(NoneTerminal.struct_specifier) export class StructSpecifier extends TreeNode { + static readonly nonTerminal = NoneTerminal.struct_specifier; ident?: BaseToken; propList: StructProp[]; macroExpressions: MacroExpression[]; @@ -1109,8 +1139,8 @@ export namespace ASTNode { } } - @ASTNodeDecorator(NoneTerminal.struct_declaration_list) export class StructDeclarationList extends TreeNode { + static readonly nonTerminal = NoneTerminal.struct_declaration_list; propList: StructProp[] = []; macroExpressions: MacroExpression[] = []; @@ -1134,8 +1164,8 @@ export namespace ASTNode { } } - @ASTNodeDecorator(NoneTerminal.struct_declaration) export class StructDeclaration extends TreeNode { + static readonly nonTerminal = NoneTerminal.struct_declaration; props: StructProp[] = []; macroExpressions: MacroExpression[] = []; @@ -1196,8 +1226,8 @@ export namespace ASTNode { } } - @ASTNodeDecorator(NoneTerminal.macro_struct_declaration) export class MacroStructDeclaration extends TreeNode { + static readonly nonTerminal = NoneTerminal.macro_struct_declaration; props: StructProp[] = []; macroExpressions: MacroExpression[] = []; @@ -1223,8 +1253,8 @@ export namespace ASTNode { } } - @ASTNodeDecorator(NoneTerminal.macro_struct_branch) export class MacroStructBranch extends TreeNode { + static readonly nonTerminal = NoneTerminal.macro_struct_branch; props: StructProp[] = []; macroExpressions: MacroExpression[] = []; @@ -1255,8 +1285,8 @@ export namespace ASTNode { } } - @ASTNodeDecorator(NoneTerminal.layout_qualifier) export class LayoutQualifier extends TreeNode { + static readonly nonTerminal = NoneTerminal.layout_qualifier; index: number; override semanticAnalyze(sa: SemanticAnalyzer): void { @@ -1264,8 +1294,8 @@ export namespace ASTNode { } } - @ASTNodeDecorator(NoneTerminal.struct_declarator_list) export class StructDeclaratorList extends TreeNode { + static readonly nonTerminal = NoneTerminal.struct_declarator_list; declaratorList: StructDeclarator[] = []; override init(): void { @@ -1289,8 +1319,8 @@ export namespace ASTNode { } } - @ASTNodeDecorator(NoneTerminal.struct_declarator) export class StructDeclarator extends TreeNode { + static readonly nonTerminal = NoneTerminal.struct_declarator; ident: BaseToken; arraySpecifier: ArraySpecifier | undefined; @@ -1305,8 +1335,8 @@ export namespace ASTNode { } } - @ASTNodeDecorator(NoneTerminal.variable_declaration) export class VariableDeclaration extends TreeNode { + static readonly nonTerminal = NoneTerminal.variable_declaration; type: FullySpecifiedType; isStatic: boolean; @@ -1337,8 +1367,8 @@ export namespace ASTNode { } } - @ASTNodeDecorator(NoneTerminal.variable_declaration_list) export class VariableDeclarationList extends TreeNode { + static readonly nonTerminal = NoneTerminal.variable_declaration_list; type: FullySpecifiedType; variableDeclarations: VariableDeclaration[] = []; @@ -1374,8 +1404,8 @@ export namespace ASTNode { } } - @ASTNodeDecorator(NoneTerminal.variable_identifier) export class VariableIdentifier extends TreeNode { + static readonly nonTerminal = NoneTerminal.variable_identifier; // @todo: typeInfo may be multiple types typeInfo: GalaceanDataType; referenceGlobalSymbolNames: string[] = []; @@ -1508,8 +1538,8 @@ export namespace ASTNode { } } - @ASTNodeDecorator(NoneTerminal.gs_shader_program) export class GLShaderProgram extends TreeNode { + static readonly nonTerminal = NoneTerminal.gs_shader_program; shaderData: ShaderData; override semanticAnalyze(sa: SemanticAnalyzer): void { @@ -1518,8 +1548,8 @@ export namespace ASTNode { } } - @ASTNodeDecorator(NoneTerminal.global_declaration) export class GlobalDeclaration extends TreeNode { + static readonly nonTerminal = NoneTerminal.global_declaration; macroExpressions: Array = []; override init(): void { @@ -1546,15 +1576,15 @@ export namespace ASTNode { } } - @ASTNodeDecorator(NoneTerminal.macro_undef) export class MacroUndef extends TreeNode { + static readonly nonTerminal = NoneTerminal.macro_undef; override codeGen(visitor: CodeGenVisitor) { return this.setCache(super.codeGen(visitor) + "\n"); } } - @ASTNodeDecorator(NoneTerminal.macro_push_context) export class MacroPushContext extends TreeNode { + static readonly nonTerminal = NoneTerminal.macro_push_context; override semanticAnalyze(sa: SemanticAnalyzer): void { sa.symbolTableStack._macroLevel++; } @@ -1564,8 +1594,8 @@ export namespace ASTNode { } } - @ASTNodeDecorator(NoneTerminal.macro_pop_context) export class MacroPopContext extends TreeNode { + static readonly nonTerminal = NoneTerminal.macro_pop_context; override semanticAnalyze(sa: SemanticAnalyzer): void { sa.symbolTableStack._macroLevel--; } @@ -1575,22 +1605,22 @@ export namespace ASTNode { } } - @ASTNodeDecorator(NoneTerminal.macro_elif_expression) export class MacroElifExpression extends TreeNode { + static readonly nonTerminal = NoneTerminal.macro_elif_expression; override codeGen(visitor: CodeGenVisitor) { return this.setCache("\n" + super.codeGen(visitor) + "\n"); } } - @ASTNodeDecorator(NoneTerminal.macro_else_expression) export class MacroElseExpression extends TreeNode { + static readonly nonTerminal = NoneTerminal.macro_else_expression; override codeGen(visitor: CodeGenVisitor) { return this.setCache("\n" + super.codeGen(visitor) + "\n"); } } - @ASTNodeDecorator(NoneTerminal.global_macro_declaration) export class GlobalMacroDeclaration extends TreeNode { + static readonly nonTerminal = NoneTerminal.global_macro_declaration; macroExpressions: MacroExpression[] = []; override init(): void { @@ -1619,8 +1649,8 @@ export namespace ASTNode { } } - @ASTNodeDecorator(NoneTerminal.global_macro_if_statement) export class GlobalMacroIfStatement extends TreeNode { + static readonly nonTerminal = NoneTerminal.global_macro_if_statement; macroExpressions: MacroExpression[] = []; override init(): void { @@ -1642,8 +1672,8 @@ export namespace ASTNode { } } - @ASTNodeDecorator(NoneTerminal.global_macro_branch) export class GlobalMacroBranch extends TreeNode { + static readonly nonTerminal = NoneTerminal.global_macro_branch; macroExpressions: MacroExpression[] = []; override init(): void { @@ -1671,14 +1701,16 @@ export namespace ASTNode { } } - @ASTNodeDecorator(NoneTerminal.macro_if_statement) - export class MacroIfStatement extends TreeNode {} + export class MacroIfStatement extends TreeNode { + static readonly nonTerminal = NoneTerminal.macro_if_statement; + } - @ASTNodeDecorator(NoneTerminal.macro_branch) - export class MacroBranch extends TreeNode {} + export class MacroBranch extends TreeNode { + static readonly nonTerminal = NoneTerminal.macro_branch; + } - @ASTNodeDecorator(NoneTerminal.macro_call_symbol) export class MacroCallSymbol extends TreeNode { + static readonly nonTerminal = NoneTerminal.macro_call_symbol; referenceSymbolNames: string[] = []; macroName: string; /** True iff every `MacroDefineInfo` visible from this call site's branch @@ -1779,8 +1811,8 @@ export namespace ASTNode { } } - @ASTNodeDecorator(NoneTerminal.macro_call_function) export class MacroCallFunction extends TreeNode { + static readonly nonTerminal = NoneTerminal.macro_call_function; referenceSymbolNames: string[] = []; macroName: string = ""; hasAstValue: boolean = false; @@ -1822,8 +1854,8 @@ export namespace ASTNode { * Semantic analysis does not resolve symbols inside the value (the definition-site * scope is not the call-site scope). Lookup happens lazily during codegen. */ - @ASTNodeDecorator(NoneTerminal.macro_define) export class MacroDefine extends TreeNode { + static readonly nonTerminal = NoneTerminal.macro_define; macroName: string; isFunction: boolean; valueExpression?: Expression; diff --git a/packages/shader-compiler/tsconfig.json b/packages/shader-compiler/tsconfig.json index f959fa90c9..a6e48bfe99 100644 --- a/packages/shader-compiler/tsconfig.json +++ b/packages/shader-compiler/tsconfig.json @@ -5,7 +5,6 @@ "declaration": true, "moduleResolution": "node", "allowSyntheticDefaultImports": true, - "experimentalDecorators": true, "declarationDir": "types", "emitDeclarationOnly": true, "noImplicitOverride": true, diff --git a/packages/shader/tsconfig.json b/packages/shader/tsconfig.json index 588b0055c3..e1fe1aa4ec 100644 --- a/packages/shader/tsconfig.json +++ b/packages/shader/tsconfig.json @@ -5,7 +5,6 @@ "declaration": true, "moduleResolution": "node", "allowSyntheticDefaultImports": true, - "experimentalDecorators": true, "declarationDir": "types", "emitDeclarationOnly": true, "noImplicitOverride": true, diff --git a/packages/spine-core-3.8/tsconfig.json b/packages/spine-core-3.8/tsconfig.json index 588b0055c3..e1fe1aa4ec 100644 --- a/packages/spine-core-3.8/tsconfig.json +++ b/packages/spine-core-3.8/tsconfig.json @@ -5,7 +5,6 @@ "declaration": true, "moduleResolution": "node", "allowSyntheticDefaultImports": true, - "experimentalDecorators": true, "declarationDir": "types", "emitDeclarationOnly": true, "noImplicitOverride": true, diff --git a/packages/spine-core-4.2/tsconfig.json b/packages/spine-core-4.2/tsconfig.json index 588b0055c3..e1fe1aa4ec 100644 --- a/packages/spine-core-4.2/tsconfig.json +++ b/packages/spine-core-4.2/tsconfig.json @@ -5,7 +5,6 @@ "declaration": true, "moduleResolution": "node", "allowSyntheticDefaultImports": true, - "experimentalDecorators": true, "declarationDir": "types", "emitDeclarationOnly": true, "noImplicitOverride": true, diff --git a/packages/spine/tsconfig.json b/packages/spine/tsconfig.json index 588b0055c3..e1fe1aa4ec 100644 --- a/packages/spine/tsconfig.json +++ b/packages/spine/tsconfig.json @@ -5,7 +5,6 @@ "declaration": true, "moduleResolution": "node", "allowSyntheticDefaultImports": true, - "experimentalDecorators": true, "declarationDir": "types", "emitDeclarationOnly": true, "noImplicitOverride": true, diff --git a/packages/ui/src/component/UICanvas.ts b/packages/ui/src/component/UICanvas.ts index 0ad1cf5524..ad11f0b4f7 100644 --- a/packages/ui/src/component/UICanvas.ts +++ b/packages/ui/src/component/UICanvas.ts @@ -398,7 +398,6 @@ export class UICanvas extends Component implements IElement, ICloneHook { }); describe("Field decorators take priority over Entity/Component remap", () => { + it("registers Stage-3 field modes on declaration metadata", () => { + const rootEntity = scene.createRootEntity("root"); + const parent = rootEntity.createChild("parent"); + const sibling = rootEntity.createChild("sibling"); + const child = parent.createChild("child"); + const prototype = Stage3DecoratedRefScript.prototype; + const prototypeSymbolsBefore = Object.getOwnPropertySymbols(prototype); + const metadataKey = (Symbol as typeof Symbol & { metadata?: symbol }).metadata!; + const classMetadata = (Stage3DecoratedRefScript as unknown as Record)[metadataKey]; + const metadataSymbolsBefore = Object.getOwnPropertySymbols(classMetadata); + const script = parent.addComponent(Stage3DecoratedRefScript); + const siblingScript = sibling.addComponent(Stage3DecoratedRefScript); + script.assignedEntity = child; + script.ignoredEntity = child; + + expect(metadataSymbolsBefore).toHaveLength(1); + expect(Object.getOwnPropertySymbols(classMetadata)).toEqual(metadataSymbolsBefore); + expect(Object.getOwnPropertySymbols(script)).toHaveLength(0); + expect(Object.getOwnPropertySymbols(siblingScript)).toHaveLength(0); + expect(Object.getOwnPropertySymbols(prototype)).toEqual(prototypeSymbolsBefore); + + const cloned = parent.clone(); + const clonedScript = cloned.getComponent(Stage3DecoratedRefScript); + expect(clonedScript.assignedEntity).eq(child); + expect(clonedScript.ignoredEntity).eq(undefined); + + rootEntity.destroy(); + }); + + it("keeps Stage-3 field modes isolated across inheritance overrides", () => { + class Stage3BaseOverrideScript extends Script { + @assignmentClone + target: Entity; + @ignoreClone + inherited: Entity; + } + class Stage3SubOverrideScript extends Stage3BaseOverrideScript { + @ignoreClone + target: Entity; + } + const metadataKey = (Symbol as typeof Symbol & { metadata?: symbol }).metadata!; + const baseMetadata = (Stage3BaseOverrideScript as unknown as Record>)[ + metadataKey + ]; + const subMetadata = (Stage3SubOverrideScript as unknown as Record>)[metadataKey]; + const baseMetadataSymbols = Object.getOwnPropertySymbols(baseMetadata); + const subMetadataSymbols = Object.getOwnPropertySymbols(subMetadata); + + expect(baseMetadataSymbols).toHaveLength(1); + expect(subMetadataSymbols).toEqual(baseMetadataSymbols); + expect(subMetadata[subMetadataSymbols[0]]).not.eq(baseMetadata[baseMetadataSymbols[0]]); + + const rootEntity = scene.createRootEntity("root"); + const external = rootEntity.createChild("external"); + const baseEntity = rootEntity.createChild("base"); + const subEntity = rootEntity.createChild("sub"); + const baseScript = baseEntity.addComponent(Stage3BaseOverrideScript); + const subScript = subEntity.addComponent(Stage3SubOverrideScript); + baseScript.target = external; + baseScript.inherited = external; + subScript.target = external; + subScript.inherited = external; + + const clonedBase = baseEntity.clone().getComponent(Stage3BaseOverrideScript); + const clonedSub = subEntity.clone().getComponent(Stage3SubOverrideScript); + expect(clonedBase.target).eq(external); + expect(clonedBase.inherited).eq(undefined); + expect(clonedSub.target).eq(undefined); + expect(clonedSub.inherited).eq(undefined); + + rootEntity.destroy(); + }); + + it("rejects unsupported Stage-3 decorator targets", () => { + expect(() => { + class StaticField { + @assignmentClone + static field: Entity; + } + return StaticField; + }).toThrowError(TypeError); + + expect(() => { + class PrivateField { + @assignmentClone + #field: Entity; + } + return PrivateField; + }).toThrowError(TypeError); + + expect(() => { + const field = Symbol("field"); + class SymbolField { + @assignmentClone + [field]: Entity; + } + return SymbolField; + }).toThrowError(TypeError); + + expect(() => { + class MethodTarget { + // @ts-expect-error Clone decorators only support fields. + @assignmentClone + method(): void {} + } + return MethodTarget; + }).toThrowError(TypeError); + }); + it("@assignmentClone entity ref shares the source reference (decorator wins)", () => { const rootEntity = scene.createRootEntity("root"); const parent = rootEntity.createChild("parent"); diff --git a/tests/vitest.config.ts b/tests/vitest.config.ts index d6c396b06e..241e602915 100644 --- a/tests/vitest.config.ts +++ b/tests/vitest.config.ts @@ -2,6 +2,9 @@ import { defineProject } from "vitest/config"; import type {} from "@vitest/browser/providers/playwright"; export default defineProject({ + esbuild: { + target: "es2022" + }, server: { port: 51204 }, diff --git a/tsconfig.json b/tsconfig.json index f62e2e508d..3287e7b4d9 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,7 +6,6 @@ "declaration": true, "moduleResolution": "node", "allowSyntheticDefaultImports": true, - "experimentalDecorators": true, "declarationDir": "types", "sourceMap": true, "baseUrl": "./", diff --git a/tsconfig.tests.json b/tsconfig.tests.json index 8c4e1ecea9..71bdc51463 100644 --- a/tsconfig.tests.json +++ b/tsconfig.tests.json @@ -3,7 +3,6 @@ "target": "ES2017", "module": "CommonJS", "moduleResolution": "node", - "experimentalDecorators": true, "esModuleInterop": true, "strict": false, "lib": [ @@ -15,4 +14,4 @@ "outDir": "lib" }, "include": ["src/*.ts"] -} \ No newline at end of file +}