From 8a23e89c870946bf279032c6ccf91eed83ed9349 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 8 Feb 2021 19:04:23 +0000 Subject: [PATCH 1/2] Convert ReEmitter to TS And also add a test so I can be confident it's actually doing the same thing. NB. There was some logic there previously to reduce the number of bound functions that had to be kept around, but it subsequently started adding the source object as the last arg, at which point there's now one bound function in memory per re-emitted event name (plus the previous per-event-name handlers). This reduces it so it's just one per re-emitted event name, so still could be quite a few, but fewer than before. --- src/ReEmitter.js | 52 ------------------------------------------------ src/ReEmitter.ts | 39 ++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 52 deletions(-) delete mode 100644 src/ReEmitter.js create mode 100644 src/ReEmitter.ts diff --git a/src/ReEmitter.js b/src/ReEmitter.js deleted file mode 100644 index 84a856bb2..000000000 --- a/src/ReEmitter.js +++ /dev/null @@ -1,52 +0,0 @@ -/* -Copyright 2015, 2016 OpenMarket Ltd -Copyright 2017 Vector Creations Ltd -Copyright 2017 New Vector Ltd - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -/** - * @module - */ - -export class ReEmitter { - constructor(target) { - this.target = target; - - // We keep one bound event handler for each event name so we know - // what event is arriving - this.boundHandlers = {}; - } - - _handleEvent(eventName, ...args) { - this.target.emit(eventName, ...args); - } - - reEmit(source, eventNames) { - // We include the source as the last argument for event handlers which may need it, - // such as read receipt listeners on the client class which won't have the context - // of the room. - const forSource = (handler, ...args) => { - handler(...args, source); - }; - for (const eventName of eventNames) { - if (this.boundHandlers[eventName] === undefined) { - this.boundHandlers[eventName] = this._handleEvent.bind(this, eventName); - } - - const boundHandler = forSource.bind(this, this.boundHandlers[eventName]); - source.on(eventName, boundHandler); - } - } -} diff --git a/src/ReEmitter.ts b/src/ReEmitter.ts new file mode 100644 index 000000000..f65912aa4 --- /dev/null +++ b/src/ReEmitter.ts @@ -0,0 +1,39 @@ +/* +Copyright 2015, 2016 OpenMarket Ltd +Copyright 2017 Vector Creations Ltd +Copyright 2017 New Vector Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import { EventEmitter } from "events"; + +export class ReEmitter { + private target: EventEmitter; + + constructor(target: EventEmitter) { + this.target = target; + } + + reEmit(source: EventEmitter, eventNames: string[]) { + for (const eventName of eventNames) { + // We include the source as the last argument for event handlers which may need it, + // such as read receipt listeners on the client class which won't have the context + // of the room. + const forSource = (...args) => { + this.target.emit(eventName, ...args, source); + }; + source.on(eventName, forSource); + } + } +} From 91290c0d25b09ead72370f7757cdeaee16678a8f Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 8 Feb 2021 19:09:32 +0000 Subject: [PATCH 2/2] Actually add the test --- spec/unit/ReEmitter.spec.ts | 49 +++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 spec/unit/ReEmitter.spec.ts diff --git a/spec/unit/ReEmitter.spec.ts b/spec/unit/ReEmitter.spec.ts new file mode 100644 index 000000000..d1c853fe6 --- /dev/null +++ b/spec/unit/ReEmitter.spec.ts @@ -0,0 +1,49 @@ +/* +Copyright 2021 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import { EventEmitter } from "events"; +import { ReEmitter } from "../../src/ReEmitter"; + +const EVENTNAME = "UnknownEntry"; + +class EventSource extends EventEmitter { + doTheThing() { + this.emit(EVENTNAME, "foo", "bar"); + } +} + +class EventTarget extends EventEmitter { + +} + +describe("ReEmitter", function() { + it("Re-Emits events with the same args", function() { + const src = new EventSource(); + const tgt = new EventTarget(); + + const handler = jest.fn(); + tgt.on(EVENTNAME, handler); + + const reEmitter = new ReEmitter(tgt); + reEmitter.reEmit(src, [EVENTNAME]); + + src.doTheThing(); + + // Args should be the args passed to 'emit' after the event name, and + // also the source object of the event which re-emitter adds + expect(handler).toHaveBeenCalledWith("foo", "bar", src); + }); +});