Skip to content

Commit 8776a4d

Browse files
Merge pull request diffblue#521 from diffblue/feature/DI-fallback
Create nondet initialisation for unresolved bean types [SEC-584]
2 parents ace6773 + c3703e8 commit 8776a4d

File tree

8 files changed

+157
-88
lines changed

8 files changed

+157
-88
lines changed

env-model-generator/src/env-model-generator.ts

+11-9
Original file line numberDiff line numberDiff line change
@@ -67,15 +67,17 @@ async function transformConfigFiles(fileName: string, options: any) {
6767
new Block(
6868
"try",
6969
(<Statement[]>[]).concat(
70-
model.Bean.getNamed().map((bean) =>
71-
new Block(
72-
`if (name.equals("${bean.name}"))`,
73-
[
74-
new Return(new FnCall(bean.getter, [])),
75-
])),
76-
[...model.Bean.getAliases()].map(
70+
model.Repository.getNamed()
71+
.sort((a, b) => a.name < b.name ? -1 : a.name === b.name ? 0 : 1)
72+
.map((bean) =>
73+
new Block(
74+
`if (name.equals("${bean.name}"))`,
75+
[
76+
new Return(new FnCall(bean.getter, [])),
77+
])),
78+
[...model.Repository.getAliases()].map(
7779
function ([ alias, beanId ]) {
78-
const bean = model.Bean.tryGet(beanId);
80+
const bean = model.Repository.tryGet(beanId);
7981
if (bean === undefined)
8082
throw new Error("Found alias to bean that doesn't exist");
8183
return new Block(
@@ -96,7 +98,7 @@ async function transformConfigFiles(fileName: string, options: any) {
9698
),
9799
new BlankLine(),
98100
new BlankLine(),
99-
..._.flatMap([...model.Bean.getAll()], (bean) => bean.javaMembers),
101+
..._.flatMap([...model.Repository.getAll()], (bean) => bean.javaMembers),
100102
new BlankLine(),
101103
...model.PropertiesValue.utilityMethods(),
102104
...model.MapValue.utilityMethods(),

env-model-generator/src/model.ts

+83-38
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,20 @@ export interface BeanProperty {
2626
}
2727

2828
interface Factory {
29-
bean: Bean;
29+
bean: IBean;
3030
method: string;
3131
}
3232

33-
export class Bean {
34-
private readonly isAnonymous: boolean;
33+
export interface IBean {
34+
readonly IsAnonymous: boolean;
35+
readonly name: string;
36+
readonly getter: string;
37+
readonly javaMembers: Member[];
38+
readonly exceptions: string[];
39+
}
40+
41+
export class Bean implements IBean {
42+
public readonly IsAnonymous: boolean;
3543
public readonly name: string;
3644
private readonly impl: string | Factory;
3745
private readonly isLazyInit: boolean;
@@ -70,16 +78,16 @@ export class Bean {
7078

7179
if (impl === undefined)
7280
throw Error(`Tried to create a Bean ('${name}') without a class name or a factory bean/method`);
73-
this.isAnonymous = name === undefined;
81+
this.IsAnonymous = name === undefined;
7482
if (name === undefined)
75-
name = Bean.getNextAnonName(typeof impl === "string" ? impl : impl.bean.identifier + "_" + impl.method);
83+
name = Bean.getNextAnonName(typeof impl === "string" ? impl : impl.bean.name + "_" + impl.method);
7684
this.name = name;
7785
this.impl = impl;
7886
this.isLazyInit = isLazyInit;
7987
this.constructorArgs = constructorArgs;
8088
this.properties = propertiesMap;
8189
this.initMethod = initMethod;
82-
Bean.beans.add(this);
90+
Repository.registerBean(this);
8391
}
8492

8593
private get identifier(): string {
@@ -172,46 +180,83 @@ export class Bean {
172180
Bean.anonIndexByClass.set(baseName, index);
173181
return baseName + index;
174182
}
183+
}
184+
185+
export class UndefinedBean implements IBean {
186+
private readonly typeName: string;
187+
public readonly name: string;
188+
public readonly IsAnonymous = true;
189+
190+
public constructor(identifier: string, typeName: string) {
191+
this.name = identifier;
192+
this.typeName = typeName;
193+
Repository.registerBean(this);
194+
Repository.registerIdForClass(this.typeName, this.name);
195+
}
196+
197+
public get getter(): string {
198+
return `get${upperInitialChar(this.name)}`;
199+
}
200+
201+
public get javaMembers(): Member[] {
202+
return [
203+
new Method(
204+
`public static ${this.typeName} ${this.getter}()`,
205+
[
206+
new Return(new FnCall("org.cprover.CProver.nondetWithNull", [])),
207+
],
208+
[]),
209+
new BlankLine(),
210+
];
211+
}
212+
213+
public readonly exceptions: string[] = [];
214+
}
175215

176-
private static beans = new NameMap<Bean>();
216+
export namespace Repository {
217+
const beans = new NameMap<IBean>();
218+
219+
export function registerBean(bean: IBean) {
220+
beans.add(bean);
221+
}
177222

178-
public static tryGet(beanId: string) {
179-
const aliasedBeanId = Bean.getAlias(beanId);
180-
return Bean.beans.get(aliasedBeanId === undefined ? beanId : aliasedBeanId);
223+
export function tryGet(beanId: string) {
224+
const aliasedBeanId = getAlias(beanId);
225+
return beans.get(aliasedBeanId === undefined ? beanId : aliasedBeanId);
181226
}
182227

183-
public static getAll(): Iterable<Bean> {
184-
return Bean.beans;
228+
export function getAll(): Iterable<IBean> {
229+
return beans;
185230
}
186231

187-
public static getNamed(): Bean[] {
188-
return [...Bean.getAll()].filter((bean) => !bean.isAnonymous);
232+
export function getNamed(): IBean[] {
233+
return [...getAll()].filter((bean) => !bean.IsAnonymous);
189234
}
190235

191-
private static aliases = new Map<string, string>();
236+
let aliases = new Map<string, string>();
192237

193-
public static getAlias(alias: string): string | undefined {
194-
return Bean.aliases.get(alias);
238+
export function getAlias(alias: string): string | undefined {
239+
return aliases.get(alias);
195240
}
196241

197-
public static addAlias(alias: string, beanId: string) {
198-
const existingAlias = Bean.getAlias(alias);
242+
export function addAlias(alias: string, beanId: string) {
243+
const existingAlias = getAlias(alias);
199244
if (existingAlias !== undefined) {
200245
if (existingAlias !== beanId)
201246
throw new ConfigParseError(`Found multiple aliases from '${alias}' to '${existingAlias}' and '${beanId}'`);
202247
// No point in adding the same alias again
203248
return;
204249
}
205-
Bean.aliases.set(alias, beanId);
250+
aliases.set(alias, beanId);
206251
}
207252

208-
public static checkAliases(beanWithIdExists: (beanId: string) => boolean): void {
253+
export function checkAliases(beanWithIdExists: (beanId: string) => boolean): void {
209254
// If A -> B is added before B -> C then A -> B needs updating to A -> C now
210-
const aliases = new Map<string, string>();
211-
for (const [ alias, target ] of Bean.aliases) {
255+
const simplifiedAliases = new Map<string, string>();
256+
for (const [ alias, target ] of aliases) {
212257
let beanId = target;
213258
while (true) {
214-
const aliasedBeanId = Bean.aliases.get(beanId);
259+
const aliasedBeanId = aliases.get(beanId);
215260
if (aliasedBeanId === undefined)
216261
break;
217262
beanId = aliasedBeanId;
@@ -220,27 +265,27 @@ export class Bean {
220265
}
221266
if (!beanWithIdExists(beanId))
222267
throw new ConfigParseError(`Couldn't find bean '${beanId}' aliased from '${alias}'`);
223-
aliases.set(alias, beanId);
268+
simplifiedAliases.set(alias, beanId);
224269
}
225-
Bean.aliases = aliases;
270+
aliases = simplifiedAliases;
226271
}
227272

228-
public static getAliases(): Iterable<[ string, string ]> {
229-
return Bean.aliases;
273+
export function getAliases(): Iterable<[ string, string ]> {
274+
return aliases;
230275
}
231276

232-
private static beanIdByClass = new MapTrackingDuplicates<string, string>();
277+
const beanIdByClass = new MapTrackingDuplicates<string, string>();
233278

234-
public static registerIdForClass(qualifiedClassName: string, beanId: string) {
235-
Bean.beanIdByClass.set(qualifiedClassName, beanId);
279+
export function registerIdForClass(qualifiedClassName: string, beanId: string) {
280+
beanIdByClass.set(qualifiedClassName, beanId);
236281
}
237282

238-
public static tryGetIdByClass(qualifiedClassName: string): string | undefined {
239-
return Bean.beanIdByClass.get(qualifiedClassName);
283+
export function tryGetIdByClass(qualifiedClassName: string): string | undefined {
284+
return beanIdByClass.get(qualifiedClassName);
240285
}
241286

242-
public static hasMultipleBeansForClass(qualifiedClassName: string): boolean {
243-
return Bean.beanIdByClass.hasDuplicates(qualifiedClassName);
287+
export function hasMultipleBeansForClass(qualifiedClassName: string): boolean {
288+
return beanIdByClass.hasDuplicates(qualifiedClassName);
244289
}
245290
}
246291

@@ -384,7 +429,7 @@ export class BeanRefValue extends Value {
384429
}
385430

386431
private get bean() {
387-
const bean = Bean.tryGet(this.beanRef);
432+
const bean = Repository.tryGet(this.beanRef);
388433
if (bean === undefined)
389434
throw new Error(`Reference created to bean '${this.beanRef}' that has no implementation`);
390435
return bean;
@@ -396,9 +441,9 @@ export class BeanRefValue extends Value {
396441
}
397442

398443
export class BeanValue extends Value {
399-
private bean: Bean;
444+
private bean: IBean;
400445

401-
public constructor(bean: Bean) {
446+
public constructor(bean: IBean) {
402447
super();
403448
this.bean = bean;
404449
}

env-model-generator/src/nameMap.ts

+12-10
Original file line numberDiff line numberDiff line change
@@ -13,46 +13,48 @@ export default class NameMap<T extends NamedObject> implements Iterable<T> {
1313
initialValues === undefined ? undefined : initialValues.map((v) => <[string, T]>[v.name, v]));
1414
}
1515

16-
public add(elt: T) {
16+
public add(elt: T): this {
1717
if (this._map.has(elt.name))
1818
throw new Error(`Tried to add object with same name '${elt.name}' as an object that already exists in this NameMap`);
1919
return this.set(elt);
2020
}
2121

22-
public set(elt: T) {
22+
public set(elt: T): this {
2323
this._map.set(elt.name, elt);
2424
return this;
2525
}
2626

27-
public addRange(values: T[]) {
27+
public addRange(values: T[]): this {
2828
for (const value of values)
2929
this.add(value);
30+
return this;
3031
}
3132

32-
public setRange(values: T[]) {
33+
public setRange(values: T[]): this {
3334
for (const value of values)
3435
this.set(value);
36+
return this;
3537
}
3638

37-
public has(name: string) {
39+
public has(name: string): boolean {
3840
return this._map.has(name);
3941
}
4042

41-
public get(name: string) {
43+
public get(name: string): T | undefined {
4244
return this._map.get(name);
4345
}
4446

45-
public delete(name: string) {
47+
public delete(name: string): this {
4648
this._map.delete(name);
4749
return this;
4850
}
4951

50-
public clear() {
52+
public clear(): this {
5153
this._map.clear();
5254
return this;
5355
}
5456

55-
public getOrAdd(name: string, generator: () => T) {
57+
public getOrAdd(name: string, generator: () => T): T {
5658
let elt = this.get(name);
5759
if (elt !== undefined)
5860
return elt;
@@ -61,7 +63,7 @@ export default class NameMap<T extends NamedObject> implements Iterable<T> {
6163
return elt;
6264
}
6365

64-
public get size() {
66+
public get size(): number {
6567
return this._map.size;
6668
}
6769

0 commit comments

Comments
 (0)