From 3779f1c9043b7e84dabd62eabbed14b9b6ea6e73 Mon Sep 17 00:00:00 2001 From: Harry Altman Date: Fri, 21 Oct 2022 20:53:51 -0400 Subject: [PATCH 01/10] Add addCompilations method to ProjectDecoder --- packages/decoder/lib/decoders.ts | 75 ++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/packages/decoder/lib/decoders.ts b/packages/decoder/lib/decoders.ts index e48b135ec3a..7486e88151d 100644 --- a/packages/decoder/lib/decoders.ts +++ b/packages/decoder/lib/decoders.ts @@ -133,6 +133,81 @@ export class ProjectDecoder { debug("done with allocation"); } + /** + * **This function is asynchronous.** + * + * Adds compilations to the decoder after it has started. Note it is + * only presently possible to do this with a `ProjectDecoder` and not + * with the other decoder classes. + * + * @param compilations The compilations to be added. Take care that these + * have IDs distinct from those the decoder already has. + */ + public async addCompilations( + compilations: Compilations.Compilation[] + ): Promise { + this.compilations = [...this.compilations, ...compilations]; + + const { + definitions: referenceDeclarations, + typesByCompilation: userDefinedTypesByCompilation, + types: userDefinedTypes + } = Compilations.Utils.collectUserDefinedTypesAndTaggedOutputs( + compilations + ); + + Object.assign(this.referenceDeclarations, referenceDeclarations); + Object.assign( + this.userDefinedTypesByCompilation, + userDefinedTypesByCompilation + ); + Object.assign(this.userDefinedTypes, userDefinedTypes); + + const { contexts, deployedContexts, contractsAndContexts, allocationInfo } = + AbiData.Allocate.Utils.collectAllocationInfo(compilations); + Object.assign(this.contexts, contexts); + Object.assign(this.deployedContexts, deployedContexts); + this.contractsAndContexts = [ + ...this.contractsAndContexts, + ...contractsAndContexts + ]; + + let allocations: Evm.AllocationInfo; + allocations.abi = AbiData.Allocate.getAbiAllocations(userDefinedTypes); + allocations.storage = Storage.Allocate.getStorageAllocations( + userDefinedTypesByCompilation + ); + allocations.calldata = AbiData.Allocate.getCalldataAllocations( + allocationInfo, + referenceDeclarations, + userDefinedTypes, + allocations.abi + ); + allocations.returndata = AbiData.Allocate.getReturndataAllocations( + allocationInfo, + referenceDeclarations, + userDefinedTypes, + allocations.abi + ); + allocations.eventdata = AbiData.Allocate.getEventAllocations( + allocationInfo, + referenceDeclarations, + userDefinedTypes, + allocations.abi + ); + allocations.state = Storage.Allocate.getStateAllocations( + allocationInfo, + referenceDeclarations, + userDefinedTypes, + allocations.storage + ); + + this.allocations = Evm.Merge.mergeAllocations( + this.allocations, + allocations + ); + } + /** * @protected */ From 3aad3e1f28f7f39724aca98a628fd86b8e88e437 Mon Sep 17 00:00:00 2001 From: Harry Altman Date: Mon, 24 Oct 2022 22:34:25 -0400 Subject: [PATCH 02/10] Replace mergeAllocations function with merges where simple and redos where complex --- packages/decoder/lib/decoders.ts | 49 +++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/packages/decoder/lib/decoders.ts b/packages/decoder/lib/decoders.ts index 7486e88151d..d7d042832f7 100644 --- a/packages/decoder/lib/decoders.ts +++ b/packages/decoder/lib/decoders.ts @@ -172,39 +172,56 @@ export class ProjectDecoder { ...contractsAndContexts ]; - let allocations: Evm.AllocationInfo; - allocations.abi = AbiData.Allocate.getAbiAllocations(userDefinedTypes); - allocations.storage = Storage.Allocate.getStorageAllocations( + //everything up till now has been pretty straightforward merges. + //but allocations are a bit more complicated. + //some of them can be merged straightforwardly, some can't. + //we'll start with the straightforward ones. + const abiAllocations = AbiData.Allocate.getAbiAllocations(userDefinedTypes); + Object.assign(this.allocations.abi, abiAllocations); + const storageAllocations = Storage.Allocate.getStorageAllocations( userDefinedTypesByCompilation ); - allocations.calldata = AbiData.Allocate.getCalldataAllocations( + Object.assign(this.allocations.storage, storageAllocations); + const stateAllocations = Storage.Allocate.getStateAllocations( allocationInfo, referenceDeclarations, userDefinedTypes, - allocations.abi + storageAllocations //only need the ones from the new compilations ); - allocations.returndata = AbiData.Allocate.getReturndataAllocations( + Object.assign(this.allocations.state, stateAllocations); + + //now we have calldata allocations. merging these is still mostly straightforward, + //but slightly less so. + const calldataAllocations = AbiData.Allocate.getCalldataAllocations( allocationInfo, referenceDeclarations, userDefinedTypes, - allocations.abi + abiAllocations //only need the ones from the new compilations + ); + //merge constructor and function allocations separately + Object.assign( + this.allocations.calldata.constructorAllocations, + calldataAllocations.constructorAllocations + ); + Object.assign( + this.allocations.calldata.functionAllocations, + calldataAllocations.functionAllocations ); - allocations.eventdata = AbiData.Allocate.getEventAllocations( + + //finally, redo the allocations for returndata and eventdata. + //attempting to perform a merge on these is too complicated, so we + //won't try; we'll just recompute. + this.allocations.returndata = AbiData.Allocate.getReturndataAllocations( allocationInfo, referenceDeclarations, userDefinedTypes, - allocations.abi + this.allocations.abi //we're doing this for merged result, so use merged input! ); - allocations.state = Storage.Allocate.getStateAllocations( + this.allocations.event = AbiData.Allocate.getEventAllocations( allocationInfo, referenceDeclarations, userDefinedTypes, - allocations.storage - ); - - this.allocations = Evm.Merge.mergeAllocations( - this.allocations, - allocations + this.allocations.abi //we're doing this for merged result, so use merged input! ); } From fe39b7a51633ee17d67dd52a112e601c2cdd4cc3 Mon Sep 17 00:00:00 2001 From: Harry Altman Date: Tue, 25 Oct 2022 21:10:46 -0400 Subject: [PATCH 03/10] Allow adding compilations in more general form --- packages/codec/lib/compilations/utils.ts | 7 ++++--- packages/decoder/lib/decoders.ts | 26 ++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/packages/codec/lib/compilations/utils.ts b/packages/codec/lib/compilations/utils.ts index 797fb867e9d..e58e4479041 100644 --- a/packages/codec/lib/compilations/utils.ts +++ b/packages/codec/lib/compilations/utils.ts @@ -674,7 +674,8 @@ function projectInfoIsArtifacts( } export function infoToCompilations( - projectInfo: ProjectInfo | undefined + projectInfo: ProjectInfo | undefined, + nonceString?: string ): Compilation[] { if (!projectInfo) { throw new NoProjectInfoError(); @@ -682,8 +683,8 @@ export function infoToCompilations( if (projectInfoIsCodecStyle(projectInfo)) { return projectInfo.compilations; } else if (projectInfoIsCommonStyle(projectInfo)) { - return shimCompilations(projectInfo.commonCompilations); + return shimCompilations(projectInfo.commonCompilations, nonceString); } else if (projectInfoIsArtifacts(projectInfo)) { - return shimArtifacts(projectInfo.artifacts); + return shimArtifacts(projectInfo.artifacts, undefined, nonceString); } } diff --git a/packages/decoder/lib/decoders.ts b/packages/decoder/lib/decoders.ts index d7d042832f7..1483a09b05c 100644 --- a/packages/decoder/lib/decoders.ts +++ b/packages/decoder/lib/decoders.ts @@ -68,6 +68,8 @@ export class ProjectDecoder { private ensSettings: DecoderTypes.EnsSettings; + private addProjectInfoNonce: number = 0; + /** * @protected */ @@ -225,6 +227,30 @@ export class ProjectDecoder { ); } + /** + * **This function is asynchronous.** + * + * Adds additional compilations to the decoder like [[addCompilations]], + * but allows it to be specified in more general forms. + * + * @param projectInfo Information about the additional compilations or + * contracts to be decoded. This may come in several forms; see the type + * documentation for more information. If passing in `{ compilations: ... }`, + * take care that the compilations have different IDs from others passed in + * so far. If passed in in another form, an ID will be assigned automatically, + * which should generally avoid any collisions. + */ + public async addAdditionalProjectInfo( + projectInfo: Compilations.ProjectInfo + ): Promise { + const compilations = Compilations.Utils.infoToCompilations( + projectInfo, + `decoderAdditionalShimmedCompilationGroup(${this.addProjectInfoNonce})` + ); + this.addProjectInfoNonce++; + await this.addCompilations(compilations); + } + /** * @protected */ From 0b63df30e0130f9ee3690bf40fac02ac167bae35 Mon Sep 17 00:00:00 2001 From: Harry Altman Date: Tue, 25 Oct 2022 22:10:08 -0400 Subject: [PATCH 04/10] Add test of adding compilations --- .../test/current/test/additional-test.js | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 packages/decoder/test/current/test/additional-test.js diff --git a/packages/decoder/test/current/test/additional-test.js b/packages/decoder/test/current/test/additional-test.js new file mode 100644 index 00000000000..e913decf1e0 --- /dev/null +++ b/packages/decoder/test/current/test/additional-test.js @@ -0,0 +1,78 @@ +const debug = require("debug")("decoder:test:additional-test"); +const assert = require("chai").assert; +const Ganache = require("ganache"); +const path = require("path"); +const Web3 = require("web3"); + +const Decoder = require("../../.."); + +const { prepareContracts } = require("../../helpers"); + +describe("Adding compilations", function () { + let provider; + let abstractions; + let web3; + + let Contracts; + + before("Create Provider", async function () { + provider = Ganache.provider({ + miner: { instamine: "strict" }, + seed: "decoder", + gasLimit: 7000000, + logging: { quiet: true } + }); + web3 = new Web3(provider); + }); + + after(async () => { + provider && (await provider.disconnect()); + }); + + before("Prepare contracts and artifacts", async function () { + this.timeout(30000); + + const prepared = await prepareContracts( + provider, + path.resolve(__dirname, "..") + ); + abstractions = prepared.abstractions; + + Contracts = [ + abstractions.WireTest, + abstractions.WireTestParent, + abstractions.WireTestLibrary, + abstractions.WireTestAbstract + ]; + }); + + it("should allow adding compilations", async function () { + const deployedContract = await abstractions.WireTestRedHerring.deployed(); + + const decoder = await Decoder.forProject({ + provider: web3.currentProvider, + projectInfo: { artifacts: Contracts } + }); + + //emit an event we can't decode + const { receipt } = await deployedContract.run(); + //attempt to decode it; this should fail + const failingEvents = await decoder.events({ + fromBlock: receipt.blockNumber, + toBlock: receipt.blockNumber + }); + assert.lengthOf(failingEvents, 1); + assert.lengthOf(failingEvents[0].decodings, 0); + //now add more info + await decoder.addAdditionalProjectInfo({ + artifacts: [abstractions.WireTestRedHerring] + }); + //attempt to decode it again, it should succeed + const suceedingEvents = await decoder.events({ + fromBlock: receipt.blockNumber, + toBlock: receipt.blockNumber + }); + assert.lengthOf(suceedingEvents, 1); + assert.lengthOf(suceedingEvents[0].decodings, 1); + }); +}); From 7057cce297619fbe53041ec950dc3d39f897e289 Mon Sep 17 00:00:00 2001 From: Harry Altman Date: Thu, 27 Oct 2022 19:57:49 -0400 Subject: [PATCH 05/10] Prefer new contexts over old contexts --- packages/decoder/lib/decoders.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/decoder/lib/decoders.ts b/packages/decoder/lib/decoders.ts index 1483a09b05c..1d36445003c 100644 --- a/packages/decoder/lib/decoders.ts +++ b/packages/decoder/lib/decoders.ts @@ -167,7 +167,9 @@ export class ProjectDecoder { const { contexts, deployedContexts, contractsAndContexts, allocationInfo } = AbiData.Allocate.Utils.collectAllocationInfo(compilations); - Object.assign(this.contexts, contexts); + this.contexts = Object.assign(contexts, this.contexts); //HACK: we want to + //prioritize new contexts over old contexts, so we do this. a proper timestamp + //system would be better, but this should hopefully do for now. Object.assign(this.deployedContexts, deployedContexts); this.contractsAndContexts = [ ...this.contractsAndContexts, From 639739669a35c7c6f30fa4ad788fb2b73afeac9a Mon Sep 17 00:00:00 2001 From: Harry Altman Date: Thu, 10 Nov 2022 22:27:14 -0500 Subject: [PATCH 06/10] Add error for repeat compilation ID --- packages/decoder/lib/decoders.ts | 37 +++++++++++++++++++++++++++++--- packages/decoder/lib/errors.ts | 13 +++++++++++ 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/packages/decoder/lib/decoders.ts b/packages/decoder/lib/decoders.ts index 1d36445003c..ebbe5fcb391 100644 --- a/packages/decoder/lib/decoders.ts +++ b/packages/decoder/lib/decoders.ts @@ -41,7 +41,8 @@ import { VariableNotFoundError, MemberNotFoundError, ArrayIndexOutOfBoundsError, - NoProviderError + NoProviderError, + RepeatCompilationIdError } from "./errors"; import { Shims } from "@truffle/compile-common"; //sorry for the untyped import, but... @@ -81,6 +82,14 @@ export class ProjectDecoder { if (!provider) { throw new NoProviderError(); } + //check for repeat compilation IDs + for (let i = 0; i < compilations.length; i++) { + for (let j = i + 1; j < compilations.length; j++) { + if (compilations[i].id === compilations[j].id) { + throw new RepeatCompilationIdError(compilations[i].id); + } + } + } this.providerAdapter = new ProviderAdapter(provider); this.compilations = compilations; this.ensSettings = ensSettings || {}; @@ -148,6 +157,27 @@ export class ProjectDecoder { public async addCompilations( compilations: Compilations.Compilation[] ): Promise { + //first: make sure we're not adding a compilation with an existing ID + const existingIds = new Set( + this.compilations.map(compilation => compilation.id) + ); + //we use a find() rather than a some() so that we can put the ID in the error + const conflictingCompilation = compilations.find(compilation => + existingIds.has(compilation.id) + ); + if (conflictingCompilation !== undefined) { + throw new RepeatCompilationIdError(conflictingCompilation.id); + } + //also: check for repeats among the ones we're adding + for (let i = 0; i < compilations.length; i++) { + for (let j = i + 1; j < compilations.length; j++) { + if (compilations[i].id === compilations[j].id) { + throw new RepeatCompilationIdError(compilations[i].id); + } + } + } + + //now: checks are over, start adding stuff this.compilations = [...this.compilations, ...compilations]; const { @@ -239,8 +269,9 @@ export class ProjectDecoder { * contracts to be decoded. This may come in several forms; see the type * documentation for more information. If passing in `{ compilations: ... }`, * take care that the compilations have different IDs from others passed in - * so far. If passed in in another form, an ID will be assigned automatically, - * which should generally avoid any collisions. + * so far, otherwise this will error. If passed in in another form, an ID + * will be assigned automatically, which should generally avoid any + * collisions. */ public async addAdditionalProjectInfo( projectInfo: Compilations.ProjectInfo diff --git a/packages/decoder/lib/errors.ts b/packages/decoder/lib/errors.ts index 89ceee14120..4fd919bc367 100644 --- a/packages/decoder/lib/errors.ts +++ b/packages/decoder/lib/errors.ts @@ -174,3 +174,16 @@ export class NoProviderError extends Error { this.name = "NoProviderError"; } } + +/** + * This error indicates there was an attempt to add multiple compilations + * with the same ID, or a compilation whose ID was already in use. + */ +export class RepeatCompilationIdError extends Error { + public id: string; + constructor(id: string) { + super(`Compilation id ${id} already in use.`); + this.id = id; + this.name = "RepeatCompilationIdError"; + } +} From 26bf271d4028e17f9dbaa46ecd972cea94701f71 Mon Sep 17 00:00:00 2001 From: Harry Altman Date: Thu, 10 Nov 2022 22:45:57 -0500 Subject: [PATCH 07/10] Add tests of repeat compilation ID errors --- .../test/current/test/additional-test.js | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/packages/decoder/test/current/test/additional-test.js b/packages/decoder/test/current/test/additional-test.js index e913decf1e0..33e6dd5f639 100644 --- a/packages/decoder/test/current/test/additional-test.js +++ b/packages/decoder/test/current/test/additional-test.js @@ -11,6 +11,7 @@ const { prepareContracts } = require("../../helpers"); describe("Adding compilations", function () { let provider; let abstractions; + let compilations; let web3; let Contracts; @@ -37,6 +38,7 @@ describe("Adding compilations", function () { path.resolve(__dirname, "..") ); abstractions = prepared.abstractions; + compilations = prepared.compilations; Contracts = [ abstractions.WireTest, @@ -75,4 +77,50 @@ describe("Adding compilations", function () { assert.lengthOf(suceedingEvents, 1); assert.lengthOf(suceedingEvents[0].decodings, 1); }); + + it("should error on supplying two compilations with the same ID on startup", async function () { + try { + await Decoder.forProject({ + provider: web3.currentProvider, + projectInfo: { compilations: [compilations[0], compilations[0]] } + }); + assert.fail("Creation should have errored"); + } catch (error) { + if (error.name !== "RepeatCompilationIdError") { + throw error; //rethrow unexpected errors + } + } + }); + + it("should error on supplying two compilations with the same ID on adding", async function () { + const decoder = await Decoder.forProject({ + provider: web3.currentProvider, + projectInfo: { compilations: [] } + }); + try { + await decoder.addAdditionalProjectInfo({ + compilations: [compilations[0], compilations[0]] + }); + assert.fail("Adding new should have errored"); + } catch (error) { + if (error.name !== "RepeatCompilationIdError") { + throw error; //rethrow unexpected errors + } + } + }); + + it("should error on supplying compilation with an existing ID", async function () { + const decoder = await Decoder.forProject({ + provider: web3.currentProvider, + projectInfo: { compilations } + }); + try { + await decoder.addAdditionalProjectInfo({ compilations }); + assert.fail("Adding new should have errored"); + } catch (error) { + if (error.name !== "RepeatCompilationIdError") { + throw error; //rethrow unexpected errors + } + } + }); }); From b756ba25ac95e037ba592ff95d2044a9e6f3fb3f Mon Sep 17 00:00:00 2001 From: Harry Altman Date: Mon, 14 Nov 2022 21:15:11 -0500 Subject: [PATCH 08/10] Factor out repeat compilation check and put it in encoder --- packages/codec/lib/compilations/utils.ts | 13 ++++++++++++ packages/codec/lib/errors.ts | 13 ++++++++++++ packages/codec/lib/index.ts | 7 ++++++- packages/decoder/lib/decoders.ts | 25 ++++++++++-------------- packages/decoder/lib/errors.ts | 13 ------------ packages/encoder/lib/encoders.ts | 8 ++++++++ 6 files changed, 50 insertions(+), 29 deletions(-) diff --git a/packages/codec/lib/compilations/utils.ts b/packages/codec/lib/compilations/utils.ts index e58e4479041..de35e91e5cc 100644 --- a/packages/codec/lib/compilations/utils.ts +++ b/packages/codec/lib/compilations/utils.ts @@ -688,3 +688,16 @@ export function infoToCompilations( return shimArtifacts(projectInfo.artifacts, undefined, nonceString); } } + +export function findRepeatCompilationId( + compilations: Compilation[] +): string | null { + for (let i = 0; i < compilations.length; i++) { + for (let j = i + 1; j < compilations.length; j++) { + if (compilations[i].id === compilations[j].id) { + return compilations[i].id; + } + } + } + return null; +} diff --git a/packages/codec/lib/errors.ts b/packages/codec/lib/errors.ts index 60f80f3ebdd..419e9b2e810 100644 --- a/packages/codec/lib/errors.ts +++ b/packages/codec/lib/errors.ts @@ -76,3 +76,16 @@ export class NoProjectInfoError extends Error { this.name = "NoProjectInfoError"; } } + +/** + * This error indicates there was an attempt to add multiple compilations + * with the same ID, or a compilation whose ID was already in use. + */ +export class RepeatCompilationIdError extends Error { + public id: string; + constructor(id: string) { + super(`Compilation id ${id} already in use.`); + this.id = id; + this.name = "RepeatCompilationIdError"; + } +} diff --git a/packages/codec/lib/index.ts b/packages/codec/lib/index.ts index 973434fecf3..6c73fe66938 100644 --- a/packages/codec/lib/index.ts +++ b/packages/codec/lib/index.ts @@ -77,7 +77,12 @@ export { decodeReturndata, decodeRevert } from "./core"; -export { DecodingError, StopDecodingError, NoProjectInfoError } from "./errors"; +export { + DecodingError, + StopDecodingError, + NoProjectInfoError, + RepeatCompilationIdError +} from "./errors"; //now: what types should we export? (other than the ones from ./format) //public-facing types for the interface diff --git a/packages/decoder/lib/decoders.ts b/packages/decoder/lib/decoders.ts index ebbe5fcb391..2aa86c57e09 100644 --- a/packages/decoder/lib/decoders.ts +++ b/packages/decoder/lib/decoders.ts @@ -41,8 +41,7 @@ import { VariableNotFoundError, MemberNotFoundError, ArrayIndexOutOfBoundsError, - NoProviderError, - RepeatCompilationIdError + NoProviderError } from "./errors"; import { Shims } from "@truffle/compile-common"; //sorry for the untyped import, but... @@ -83,12 +82,10 @@ export class ProjectDecoder { throw new NoProviderError(); } //check for repeat compilation IDs - for (let i = 0; i < compilations.length; i++) { - for (let j = i + 1; j < compilations.length; j++) { - if (compilations[i].id === compilations[j].id) { - throw new RepeatCompilationIdError(compilations[i].id); - } - } + const repeatId = + Codec.Compilations.Utils.findRepeatCompilationId(compilations); + if (repeatId !== null) { + throw new Codec.RepeatCompilationIdError(repeatId); } this.providerAdapter = new ProviderAdapter(provider); this.compilations = compilations; @@ -166,15 +163,13 @@ export class ProjectDecoder { existingIds.has(compilation.id) ); if (conflictingCompilation !== undefined) { - throw new RepeatCompilationIdError(conflictingCompilation.id); + throw new Codec.RepeatCompilationIdError(conflictingCompilation.id); } //also: check for repeats among the ones we're adding - for (let i = 0; i < compilations.length; i++) { - for (let j = i + 1; j < compilations.length; j++) { - if (compilations[i].id === compilations[j].id) { - throw new RepeatCompilationIdError(compilations[i].id); - } - } + const repeatId = + Codec.Compilations.Utils.findRepeatCompilationId(compilations); + if (repeatId !== null) { + throw new Codec.RepeatCompilationIdError(repeatId); } //now: checks are over, start adding stuff diff --git a/packages/decoder/lib/errors.ts b/packages/decoder/lib/errors.ts index 4fd919bc367..89ceee14120 100644 --- a/packages/decoder/lib/errors.ts +++ b/packages/decoder/lib/errors.ts @@ -174,16 +174,3 @@ export class NoProviderError extends Error { this.name = "NoProviderError"; } } - -/** - * This error indicates there was an attempt to add multiple compilations - * with the same ID, or a compilation whose ID was already in use. - */ -export class RepeatCompilationIdError extends Error { - public id: string; - constructor(id: string) { - super(`Compilation id ${id} already in use.`); - this.id = id; - this.name = "RepeatCompilationIdError"; - } -} diff --git a/packages/encoder/lib/encoders.ts b/packages/encoder/lib/encoders.ts index e4097256e36..baead0cb130 100644 --- a/packages/encoder/lib/encoders.ts +++ b/packages/encoder/lib/encoders.ts @@ -96,6 +96,14 @@ export class ProjectEncoder { if (!info.compilations) { throw new NoInternalInfoError(); } + //check for repeat compilation IDs + const repeatId = Codec.Compilations.Utils.findRepeatCompilationId( + info.compilations + ); + if (repeatId !== null) { + throw new Codec.RepeatCompilationIdError(repeatId); + } + //since that's good, save it and continue this.compilations = info.compilations; ({ definitions: this.referenceDeclarations, From d7715e8f27896bd4e2551a2e4e4dfd8fc66263c3 Mon Sep 17 00:00:00 2001 From: Harry Altman Date: Mon, 14 Nov 2022 22:06:50 -0500 Subject: [PATCH 09/10] Return all overlapping IDs in error --- packages/codec/lib/compilations/utils.ts | 9 +++++---- packages/codec/lib/errors.ts | 8 ++++---- packages/decoder/lib/decoders.ts | 25 ++++++++++++------------ packages/encoder/lib/encoders.ts | 6 +++--- 4 files changed, 24 insertions(+), 24 deletions(-) diff --git a/packages/codec/lib/compilations/utils.ts b/packages/codec/lib/compilations/utils.ts index de35e91e5cc..bf9071feff3 100644 --- a/packages/codec/lib/compilations/utils.ts +++ b/packages/codec/lib/compilations/utils.ts @@ -689,15 +689,16 @@ export function infoToCompilations( } } -export function findRepeatCompilationId( +export function findRepeatCompilationIds( compilations: Compilation[] -): string | null { +): Set { + let repeats: Set = new Set(); for (let i = 0; i < compilations.length; i++) { for (let j = i + 1; j < compilations.length; j++) { if (compilations[i].id === compilations[j].id) { - return compilations[i].id; + repeats.add(compilations[i].id); } } } - return null; + return repeats; } diff --git a/packages/codec/lib/errors.ts b/packages/codec/lib/errors.ts index 419e9b2e810..4a03d8629b7 100644 --- a/packages/codec/lib/errors.ts +++ b/packages/codec/lib/errors.ts @@ -82,10 +82,10 @@ export class NoProjectInfoError extends Error { * with the same ID, or a compilation whose ID was already in use. */ export class RepeatCompilationIdError extends Error { - public id: string; - constructor(id: string) { - super(`Compilation id ${id} already in use.`); - this.id = id; + public ids: string[]; + constructor(ids: string[]) { + super(`Compilation id(s) ${ids.join(", ")} repeated or already in use.`); + this.ids = ids; this.name = "RepeatCompilationIdError"; } } diff --git a/packages/decoder/lib/decoders.ts b/packages/decoder/lib/decoders.ts index 2aa86c57e09..9c7e21b336a 100644 --- a/packages/decoder/lib/decoders.ts +++ b/packages/decoder/lib/decoders.ts @@ -82,10 +82,10 @@ export class ProjectDecoder { throw new NoProviderError(); } //check for repeat compilation IDs - const repeatId = - Codec.Compilations.Utils.findRepeatCompilationId(compilations); - if (repeatId !== null) { - throw new Codec.RepeatCompilationIdError(repeatId); + const repeatIds = + Codec.Compilations.Utils.findRepeatCompilationIds(compilations); + if (repeatIds.size !== 0) { + throw new Codec.RepeatCompilationIdError([...repeatIds]); } this.providerAdapter = new ProviderAdapter(provider); this.compilations = compilations; @@ -158,18 +158,17 @@ export class ProjectDecoder { const existingIds = new Set( this.compilations.map(compilation => compilation.id) ); + const newIds = new Set(compilations.map(compilation => compilation.id)); //we use a find() rather than a some() so that we can put the ID in the error - const conflictingCompilation = compilations.find(compilation => - existingIds.has(compilation.id) - ); - if (conflictingCompilation !== undefined) { - throw new Codec.RepeatCompilationIdError(conflictingCompilation.id); + const overlappingIds = [...newIds].filter(id => existingIds.has(id)); + if (overlappingIds.length !== 0) { + throw new Codec.RepeatCompilationIdError(overlappingIds); } //also: check for repeats among the ones we're adding - const repeatId = - Codec.Compilations.Utils.findRepeatCompilationId(compilations); - if (repeatId !== null) { - throw new Codec.RepeatCompilationIdError(repeatId); + const repeatIds = + Codec.Compilations.Utils.findRepeatCompilationIds(compilations); + if (repeatIds.size !== 0) { + throw new Codec.RepeatCompilationIdError([...repeatIds]); } //now: checks are over, start adding stuff diff --git a/packages/encoder/lib/encoders.ts b/packages/encoder/lib/encoders.ts index baead0cb130..60d2b82fbd9 100644 --- a/packages/encoder/lib/encoders.ts +++ b/packages/encoder/lib/encoders.ts @@ -97,11 +97,11 @@ export class ProjectEncoder { throw new NoInternalInfoError(); } //check for repeat compilation IDs - const repeatId = Codec.Compilations.Utils.findRepeatCompilationId( + const repeatIds = Codec.Compilations.Utils.findRepeatCompilationIds( info.compilations ); - if (repeatId !== null) { - throw new Codec.RepeatCompilationIdError(repeatId); + if (repeatIds.size !== 0) { + throw new Codec.RepeatCompilationIdError([...repeatIds]); } //since that's good, save it and continue this.compilations = info.compilations; From 360fc65b1fe40a8a50b842442f72748ddf6e8c0b Mon Sep 17 00:00:00 2001 From: Harry Altman Date: Mon, 14 Nov 2022 22:07:51 -0500 Subject: [PATCH 10/10] Add test that overlapping id array has correct length --- packages/decoder/test/current/test/additional-test.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/decoder/test/current/test/additional-test.js b/packages/decoder/test/current/test/additional-test.js index 33e6dd5f639..90d05cf3503 100644 --- a/packages/decoder/test/current/test/additional-test.js +++ b/packages/decoder/test/current/test/additional-test.js @@ -89,6 +89,7 @@ describe("Adding compilations", function () { if (error.name !== "RepeatCompilationIdError") { throw error; //rethrow unexpected errors } + assert.lengthOf(error.ids, 1); } }); @@ -106,6 +107,7 @@ describe("Adding compilations", function () { if (error.name !== "RepeatCompilationIdError") { throw error; //rethrow unexpected errors } + assert.lengthOf(error.ids, 1); } }); @@ -121,6 +123,7 @@ describe("Adding compilations", function () { if (error.name !== "RepeatCompilationIdError") { throw error; //rethrow unexpected errors } + assert.lengthOf(error.ids, 1); } }); });