cocos-engine icon indicating copy to clipboard operation
cocos-engine copied to clipboard

Event System _cachedArray is shared, nested dispatch causes bug

Open smallmain opened this issue 10 months ago • 0 comments

Cocos Creator version

3.8.2

System information

MacOS

Issue description

node-event-processor.ts:

    public dispatchEvent (event: Event): void {
        const owner = this.node;
        let target: Node;
        let i = 0;
        event.target = owner;

        // Event.CAPTURING_PHASE
        _cachedArray.length = 0;
        this.getCapturingTargets(event.type, _cachedArray);
        // capturing
        event.eventPhase = 1;
        for (i = _cachedArray.length - 1; i >= 0; --i) {
            target = _cachedArray[i];
            if (target.eventProcessor.capturingTarget) {
                event.currentTarget = target;
                // fire event
                target.eventProcessor.capturingTarget.emit(event.type, event, _cachedArray);
                // check if propagation stopped
                if (event.propagationStopped) {
                    _cachedArray.length = 0;
                    return;
                }
            }
        }
        _cachedArray.length = 0;

        // Event.AT_TARGET
        // checks if destroyed in capturing callbacks
        event.eventPhase = 2;
        event.currentTarget = owner;
        if (this.capturingTarget) {
            this.capturingTarget.emit(event.type, event);
        }
        if (!event.propagationImmediateStopped && this.bubblingTarget) {
            this.bubblingTarget.emit(event.type, event);
        }

        if (!event.propagationStopped && event.bubbles) {
            // Event.BUBBLING_PHASE
            this.getBubblingTargets(event.type, _cachedArray);
            // propagate
            event.eventPhase = 3;
            for (i = 0; i < _cachedArray.length; ++i) {
                target = _cachedArray[i];
                if (target.eventProcessor.bubblingTarget) {
                    event.currentTarget = target;
                    // fire event
                    target.eventProcessor.bubblingTarget.emit(event.type, event);
                    // check if propagation stopped
                    if (event.propagationStopped) {
                        _cachedArray.length = 0;
                        return;
                    }
                }
            }
        }
        _cachedArray.length = 0;
    }

If an event is dispatched during the capture or bubbling phase, the event also has a "capture" type listener, which will result in an error.

When there are more than two objects listening to capture events, target is null because the array is cleared.

            if (target.eventProcessor.capturingTarget) {       <- crash there.

Relevant error log output

No response

Steps to reproduce

.

Minimal reproduction project

No response

smallmain avatar Apr 19 '24 10:04 smallmain