Skip to content

Commit 12da8a2

Browse files
authored
Merge pull request #1389 from WebFuzzing/refactor-set-from-gene
Refactor set from gene
2 parents f8fe979 + 64bfdee commit 12da8a2

101 files changed

Lines changed: 882 additions & 1749 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

core/src/main/kotlin/org/evomaster/core/problem/externalservice/httpws/service/HarvestActualHttpWsResponseHandler.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -532,10 +532,11 @@ class HarvestActualHttpWsResponseHandler {
532532
try {
533533
val template = getACopyOfItsActualResponseIfExist(geneToMutate, probability)?.responseBody ?: return false
534534

535-
val v = ParamUtil.getValueGene(geneToMutate)
536-
val t = ParamUtil.getValueGene(template)
535+
val v = geneToMutate.getLeafGene()
536+
val t = template.getLeafGene()
537537
if (v::class.java == t::class.java) {
538538
v.copyValueFrom(t)
539+
v.forceNewTaints()
539540
return true
540541
} else if (v is StringGene) {
541542
// add template as part of specialization

core/src/main/kotlin/org/evomaster/core/problem/rest/data/RestCallAction.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,9 @@ class RestCallAction(
191191
For example, they could be a ChoiceGene when dealing with "examples" or Regex when having patterns
192192
only defined on some endpoints
193193
*/
194-
parameters[i].primaryGene().copyValueFrom(k.primaryGene())
194+
val g = parameters[i].primaryGene()
195+
g.copyValueFrom(k.primaryGene())
196+
g.forceNewTaints()
195197
}
196198
}
197199
}

core/src/main/kotlin/org/evomaster/core/problem/util/BindingBuilder.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,14 +109,14 @@ object BindingBuilder {
109109
}
110110

111111
private fun bindValues(p: Pair<Gene,Gene>, doBuildBindingGene: Boolean){
112-
val ok = p.first.setFromDifferentGene(p.second)
112+
val ok = p.first.copyValueFrom(p.second)
113113
if (ok && doBuildBindingGene){
114114
p.first.addBindingGene(p.second)
115115
p.second.addBindingGene(p.first)
116116
}
117117

118-
val first = ParamUtil.getValueGene(p.first)
119-
val second = ParamUtil.getValueGene(p.second)
118+
val first = p.first.getLeafGene()
119+
val second = p.second.getLeafGene()
120120
if(ok && !doBuildBindingGene && first is StringGene && TaintInputName.isTaintInput(first.value)){
121121
//do not use same tainted value in non-bound genes
122122
if(second is StringGene){

core/src/main/kotlin/org/evomaster/core/problem/util/ParamUtil.kt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import org.evomaster.core.search.gene.numeric.LongGene
1616
import org.evomaster.core.search.gene.wrapper.CustomMutationRateGene
1717
import org.evomaster.core.search.gene.wrapper.OptionalGene
1818
import org.evomaster.core.search.gene.string.StringGene
19-
import org.evomaster.core.search.gene.utils.GeneUtils
2019

2120
/**
2221
* this class used to handle binding values among params
@@ -100,7 +99,7 @@ class ParamUtil {
10099
*/
101100
fun compareGenesWithValue(geneA: Gene, geneB: Gene): Boolean {
102101
val geneAWithGeneBType = geneB.copy()
103-
geneAWithGeneBType.setFromDifferentGene(geneA)
102+
geneAWithGeneBType.copyValueFrom(geneA)
104103
return when (geneB) {
105104
is StringGene -> geneB.value == (geneAWithGeneBType as StringGene).value
106105
is IntegerGene -> geneB.value == (geneAWithGeneBType as IntegerGene).value
@@ -275,7 +274,7 @@ class ParamUtil {
275274

276275
fun generateParamId(list: Array<String>): String = list.joinToString(separator)
277276

278-
@Deprecated(message = "Rather use GeneUtils.getWrappedValueGene(gene)",
277+
@Deprecated(message = "Rather use getLeafGene()",
279278
replaceWith = ReplaceWith("GeneUtils.getWrappedValueGene(gene)"))
280279
fun getValueGene(gene: Gene): Gene {
281280
return gene.getLeafGene()!!

core/src/main/kotlin/org/evomaster/core/search/Individual.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -527,7 +527,7 @@ abstract class Individual(
527527
y !=x && !y.hasAnyBindingRelationship(x)
528528
}
529529
}){
530-
errors.add("Taint id ${d.key} has duplicate genes that are not related}")
530+
errors.add("Taint id ${d.key} has duplicate genes that are not related")
531531
}
532532
}
533533
return errors

core/src/main/kotlin/org/evomaster/core/search/action/Action.kt

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -79,11 +79,7 @@ abstract class Action(children: List<StructuralElement>) : ActionComponent(
7979

8080
fun forceNewTaints(){
8181
seeTopGenes().forEach { g ->
82-
g.flatView().forEach { r ->
83-
if(r is TaintableGene && !r.isDependentTaint()){
84-
r.forceNewTaintId()
85-
}
86-
}
82+
g.forceNewTaints()
8783
}
8884
}
8985

core/src/main/kotlin/org/evomaster/core/search/gene/BooleanGene.kt

Lines changed: 5 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ class BooleanGene(
3333
this.value = value.toBoolean()
3434
}
3535

36-
override fun setValueBasedOn(value: String) : Boolean{
36+
override fun unsafeSetFromStringValue(value: String) : Boolean{
3737
try{
3838
this.value = value.toBoolean()
3939
return true
@@ -62,20 +62,6 @@ class BooleanGene(
6262
return value.toString()
6363
}
6464

65-
override fun copyValueFrom(other: Gene): Boolean {
66-
if (other !is BooleanGene) {
67-
throw IllegalArgumentException("Invalid gene type ${other.javaClass}")
68-
}
69-
val current = this.value
70-
this.value = other.value
71-
if (!isLocallyValid()){
72-
this.value = current
73-
return false
74-
}
75-
76-
return true
77-
}
78-
7965
override fun containsSameValueAs(other: Gene): Boolean {
8066
if (other !is BooleanGene) {
8167
throw IllegalArgumentException("Invalid gene type ${other.javaClass}")
@@ -84,10 +70,10 @@ class BooleanGene(
8470
}
8571

8672

87-
override fun setValueBasedOn(gene: Gene): Boolean {
88-
if (gene is SeededGene<*>){
89-
return this.setValueBasedOn(gene.getPhenotype()as Gene)
90-
}
73+
override fun unsafeCopyValueFrom(other: Gene): Boolean {
74+
75+
val gene = other.getPhenotype()
76+
9177
if (gene !is BooleanGene){
9278
LoggingUtil.uniqueWarn(log, "Do not support to bind boolean gene with the type: ${gene::class.java.simpleName}")
9379
return false

core/src/main/kotlin/org/evomaster/core/search/gene/Gene.kt

Lines changed: 62 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,9 @@ import org.evomaster.core.Lazy
1515
import org.evomaster.core.problem.enterprise.EnterpriseIndividual
1616
import org.evomaster.core.problem.enterprise.SampleType
1717
import org.evomaster.core.search.RootElement
18-
import org.evomaster.core.search.gene.sql.SqlAutoIncrementGene
19-
import org.evomaster.core.search.gene.sql.SqlPrimaryKeyGene
18+
import org.evomaster.core.search.gene.interfaces.TaintableGene
2019
import org.evomaster.core.search.gene.utils.GeneUtils
21-
import org.evomaster.core.search.gene.wrapper.CustomMutationRateGene
22-
import org.evomaster.core.search.gene.wrapper.FlexibleGene
23-
import org.evomaster.core.search.gene.wrapper.NullableGene
24-
import org.evomaster.core.search.gene.wrapper.OptionalGene
25-
import org.evomaster.core.search.gene.wrapper.SelectableWrapperGene
26-
import org.evomaster.core.search.gene.wrapper.WrapperGene
20+
import org.evomaster.core.search.gene.interfaces.WrapperGene
2721
import org.evomaster.core.search.service.SearchGlobalState
2822
import org.evomaster.core.search.service.monitor.ProcessMonitorExcludeField
2923

@@ -349,6 +343,17 @@ abstract class Gene(
349343
return this
350344
}
351345

346+
/**
347+
* Return the gene used for the phenotype.
348+
* Most of the time this would be `this` gene.
349+
* Note, it is different from the concept of "wrapper", as the wrapper itself can impact
350+
* the phenotype.
351+
*
352+
* This is mainly used to handle very special cases such as [StringGene] and [SeededGene]
353+
*/
354+
open fun getPhenotype() : Gene{
355+
return this
356+
}
352357

353358

354359
protected fun matchingClass(klass: Class<*>, strict: Boolean): Boolean {
@@ -906,19 +911,22 @@ abstract class Gene(
906911
* sync [bindingGenes] based on [this]
907912
*/
908913
fun syncBindingGenesBasedOnThis(all: MutableSet<Gene> = mutableSetOf()) {
909-
if (bindingGenes.isEmpty()) return
914+
if (bindingGenes.isEmpty()){
915+
return
916+
}
910917
all.add(this)
911918
bindingGenes.filterNot { all.contains(it) }.forEach { b ->
912919
all.add(b)
913-
if (!b.setValueBasedOn(this))
914-
LoggingUtil.uniqueWarn(
915-
log,
916-
"fail to bind the gene (${b.name} with the type ${b::class.java.simpleName}) based on this gene (${this.name} with ${this::class.java.simpleName})"
917-
)
920+
if (!b.copyValueFrom(this)) {
921+
LoggingUtil.uniqueWarn(log, "fail to bind the gene (${b.name} with the type ${b::class.java.simpleName})" +
922+
" based on this gene (${this.name} with ${this::class.java.simpleName})")
923+
}
918924
b.syncBindingGenesBasedOnThis(all)
919925
}
920926

921-
children.filterNot { all.contains(it) }.forEach { it.syncBindingGenesBasedOnThis(all) }
927+
children.filterNot { all.contains(it) }.forEach {
928+
it.syncBindingGenesBasedOnThis(all)
929+
}
922930
}
923931

924932
/**
@@ -1110,62 +1118,52 @@ abstract class Gene(
11101118
* The type of genes can be different.
11111119
* However, there is no check if constraints are kept satisfied.
11121120
* So, this method should not be called directly.
1113-
* Rather use [setFromDifferentGene], which internally it calls this method,
1121+
* Rather use [copyValueFrom], which internally it calls this method,
11141122
* and then revert in case of constraint violations.
11151123
*
1116-
* @return whether the binding performs successfully.
1124+
* @return whether the value is copied based on [other] successfully.
1125+
* This is based only on the gene type.
1126+
* _WARNING_:
1127+
* - `true` might still leave the gene in an inconsistent state (ie violated constraints)
1128+
* - `false` might still apply partial updates (eg, think of an objects with several fields)
1129+
*
1130+
* Do not call directly outside this package. Call [copyValueFrom]
11171131
*
11181132
* TODO unfortunately, Kotlin has major design flows that do not allow package-level and true protected-level
11191133
* scope, like in Java :(
11201134
* This is a case in which is much worse than Java.
11211135
* But it could be simulated with Detekt and a rule like @PackagePrivate
11221136
*/
1123-
@Deprecated("Do not call directly outside this package. Call setFromDifferentGene")
1124-
//TODO remove deprecated once we integrate @PackagePrivate
1125-
internal abstract fun setValueBasedOn(gene: Gene): Boolean
1137+
abstract fun unsafeCopyValueFrom(other: Gene): Boolean
11261138

11271139

1128-
/**
1129-
* copy value based on [other]
1130-
* in some case, the [other] might not satisfy constraints of [this gene],
1131-
* then copying will not be performed successfully
1132-
*
1133-
* FIXME unclear if side-effects or not
1134-
*
1135-
* @return whether the value is copied based on [other] successfully
1136-
*/
1137-
abstract fun copyValueFrom(other: Gene): Boolean
1138-
1140+
fun forceNewTaints(){
1141+
flatView().forEach { r ->
1142+
if(r is TaintableGene && !r.isDependentTaint()){
1143+
r.forceNewTaintId()
1144+
}
1145+
}
1146+
}
11391147

11401148
/**
1141-
* Update current value of this gene, base on other gene.
1142-
* This is not [copyValueFrom], as the gene could be different.
1143-
* FIXME that comment seems wrong
1149+
* Update current value of this gene, base on [other] gene.
11441150
* If for any reason the update fails, there is not going to be any side-effects.
1151+
* A successful update must guarantee that the gene remains valid (ie, no violated constraints).
11451152
*
11461153
* @return if the update was successful
11471154
*/
1148-
fun setFromDifferentGene(gene: Gene, undoIfUpdateFails: Boolean = true): Boolean {
1149-
1150-
//FIXME current implementation leads to infinite loops. must fix copyValueFrom
1151-
//return updateValueOnlyIfValid( { setValueBasedOn(gene) } , undoIfUpdateFails)
1152-
//TODO update once fixed
1153-
return setValueBasedOn(gene)
1155+
fun copyValueFrom(gene: Gene): Boolean {
1156+
return updateValueOnlyIfValid( { unsafeCopyValueFrom(gene) }, true)
11541157
}
11551158

1156-
/*
1157-
FIXME: looks like redundancies and inconsistencies between copyValueFrom and setFromDifferentGene.
1158-
TODO once fixed, update
1159-
*/
1160-
11611159
/**
11621160
* Given a string value, apply it to the current state of this gene (and possibly recursively to its children).
11631161
* If it fails for any reason, return false.
11641162
* This method guarantees the validity of the resulting gene, ie, changes are reverted if any constraints
11651163
* is violated.
11661164
*/
11671165
fun setFromStringValue(value: String, undoIfUpdateFails: Boolean = true): Boolean {
1168-
return updateValueOnlyIfValid({ setValueBasedOn(value) }, undoIfUpdateFails)
1166+
return updateValueOnlyIfValid({ unsafeSetFromStringValue(value) }, undoIfUpdateFails)
11691167
}
11701168

11711169
/**
@@ -1181,33 +1179,42 @@ abstract class Gene(
11811179
* TODO @PackagePrivate
11821180
*/
11831181
@Deprecated("Do not call directly outside this package. Call setFromStringValue")
1184-
internal open fun setValueBasedOn(value: String): Boolean {
1182+
internal open fun unsafeSetFromStringValue(value: String): Boolean {
11851183
//TODO in future this should be abstract, to force each gene to handle it.
11861184
//few implementations can be based on AbstractParser class for Postman
11871185
throw IllegalStateException("setValueBasedOn() is not implemented for gene ${this::class.simpleName}")
11881186
}
11891187

11901188

11911189
/**
1192-
* here `valid` means that 1) [updateValue] performs correctly, ie, returns true AND 2) isLocallyValid is true
1190+
* here `valid` means that 1) [updateValue] performs correctly, ie, returns true AND 2) [isGloballyValid] is true
11931191
*
11941192
* @param updateValue lambda performs update of value of the gene
1195-
* @param undoIfUpdateFails represents whether it needs to undo the value update if [undoIfUpdateFails] returns false
1193+
* @param undoIfUpdateFails represents whether it needs to undo the value update if [updateValue] returns false
11961194
*
1197-
* @return if the value is updated with [updateValue]
1195+
* @return if the value is updated with [updateValue]. note if for any reason the current gene was not valid,
1196+
* the validity of the update will not be checked.
11981197
*/
11991198
fun updateValueOnlyIfValid(updateValue: () -> Boolean, undoIfUpdateFails: Boolean): Boolean {
1199+
1200+
if(!undoIfUpdateFails) {
1201+
return updateValue()
1202+
}
1203+
1204+
val currentlyValid = isGloballyValid()
1205+
12001206
val current = copy()
12011207
val ok = updateValue()
1202-
if (!ok && !undoIfUpdateFails) return false
12031208

1204-
if (!ok || !isLocallyValid()) {
1205-
val success = copyValueFrom(current)
1206-
assert(success)
1209+
if (!ok || (currentlyValid && !isGloballyValid())) {
1210+
//revert back
1211+
val success = unsafeCopyValueFrom(current)
1212+
//reversion should always work... if fails, it is a bug
1213+
// FIXME put back once all are implemented, eg, TaintedMapGene currently missing
1214+
//assert(success)
12071215
return false
12081216
}
12091217
return true
1210-
12111218
}
12121219

12131220
/**

0 commit comments

Comments
 (0)