Skip to content

Commit 9534260

Browse files
fix object assertions for multiply-inherited objects and put it under a flag
add comment .
1 parent ccbe6e3 commit 9534260

File tree

7 files changed

+58
-9
lines changed

7 files changed

+58
-9
lines changed

sjsonnet/src-jvm-native/sjsonnet/Config.scala

+5
Original file line numberDiff line numberDiff line change
@@ -133,4 +133,9 @@ case class Config(
133133
doc = """Raise an error if import expressions are used without proper parentheses, e.g. import "foo".bar rather than (import "foo").bar"""
134134
)
135135
strictImportSyntax: Flag = Flag(),
136+
@arg(
137+
name = "strict-inherited-assertions",
138+
doc = """Properly handle assertions defined in a Jsonnet dictionary that is extended more than once"""
139+
)
140+
strictInheritedAssertions: Flag = Flag(),
136141
)

sjsonnet/src-jvm-native/sjsonnet/SjsonnetMain.scala

+2-1
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,8 @@ object SjsonnetMain {
216216
strict = config.strict.value,
217217
noStaticErrors = config.noStaticErrors.value,
218218
noDuplicateKeysInComprehension = config.noDuplicateKeysInComprehension.value,
219-
strictImportSyntax = config.strictImportSyntax.value
219+
strictImportSyntax = config.strictImportSyntax.value,
220+
strictInheritedAssertions = config.strictInheritedAssertions.value
220221
),
221222
storePos = if (config.yamlDebug.value) currentPos = _ else null,
222223
warnLogger = warnLogger,

sjsonnet/src/sjsonnet/Evaluator.scala

+16-2
Original file line numberDiff line numberDiff line change
@@ -513,8 +513,22 @@ class Evaluator(resolver: CachedResolver,
513513
} else createNewScope(self, sup)
514514
}
515515

516-
def assertions(self: Val.Obj): Unit = if (!asserting) {
517-
asserting = true
516+
def assertions(self: Val.Obj): Unit = if (
517+
// We need to avoid asserting the same object more than once to prevent
518+
// infinite recursion, but the previous implementation had the `asserting`
519+
// flag kept under the object's *instantiation* rather than under the
520+
// object itself. That means that objects that are instantiated once then
521+
// extended multiple times would only trigger the assertions once, rather
522+
// than once per extension.
523+
//
524+
// Due to backwards compatibility concerns, this fix has to go in under
525+
// the flag `strictInheritedAssertions`, to be removed after an appropriate
526+
// time period has passed.
527+
(settings.strictInheritedAssertions && !self.asserting) ||
528+
(!settings.strictInheritedAssertions && !asserting)
529+
) {
530+
if (settings.strictInheritedAssertions) self.asserting = true
531+
else asserting = true
518532
val newScope: ValScope = makeNewScope(self, self.getSuper)
519533
var i = 0
520534
while(i < asserts.length) {

sjsonnet/src/sjsonnet/Settings.scala

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ class Settings(
88
val noStaticErrors: Boolean = false,
99
val noDuplicateKeysInComprehension: Boolean = false,
1010
val strictImportSyntax: Boolean = false,
11+
val strictInheritedAssertions: Boolean = false,
1112
)
1213

1314
object Settings {

sjsonnet/src/sjsonnet/Val.scala

+1
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ object Val{
145145
`super`: Obj,
146146
valueCache: mutable.HashMap[Any, Val] = mutable.HashMap.empty[Any, Val],
147147
private[this] var allKeys: util.LinkedHashMap[String, java.lang.Boolean] = null) extends Literal with Expr.ObjBody {
148+
var asserting: Boolean = false
148149

149150
def getSuper = `super`
150151

sjsonnet/test/src/sjsonnet/EvaluatorTests.scala

+17
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,23 @@ object EvaluatorTests extends TestSuite{
383383
test - assert(evalErr("""{assert false} + {} + {}""").contains("sjsonnet.Error: Assertion failed"))
384384
test - assert(evalErr("""{} + {assert false} + {}""").contains("sjsonnet.Error: Assertion failed"))
385385
test - assert(evalErr("""{} + {} + {assert false}""").contains("sjsonnet.Error: Assertion failed"))
386+
test - {
387+
val problematicStrictInheritedAssertionsSnippet =
388+
"""local template = { assert self.flag };
389+
|{ a: template { flag: true, }, b: template { flag: false, } }
390+
|""".stripMargin
391+
assert(
392+
evalErr(
393+
problematicStrictInheritedAssertionsSnippet,
394+
strictInheritedAssertions = true
395+
).contains("sjsonnet.Error: Assertion failed")
396+
)
397+
398+
assert(
399+
eval(problematicStrictInheritedAssertionsSnippet, strictInheritedAssertions = false) ==
400+
ujson.Obj("a" -> ujson.Obj("flag" -> true), "b" -> ujson.Obj("flag" -> false))
401+
)
402+
}
386403
}
387404
}
388405
}

sjsonnet/test/src/sjsonnet/TestUtils.scala

+16-6
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ object TestUtils {
44
def eval0(s: String,
55
preserveOrder: Boolean = false,
66
strict: Boolean = false,
7-
noDuplicateKeysInComprehension: Boolean = false) = {
7+
noDuplicateKeysInComprehension: Boolean = false,
8+
strictInheritedAssertions: Boolean = false) = {
89
new Interpreter(
910
Map(),
1011
Map(),
@@ -14,20 +15,29 @@ object TestUtils {
1415
new Settings(
1516
preserveOrder = preserveOrder,
1617
strict = strict,
17-
noDuplicateKeysInComprehension = noDuplicateKeysInComprehension
18+
noDuplicateKeysInComprehension = noDuplicateKeysInComprehension,
19+
strictInheritedAssertions = strictInheritedAssertions
1820
)
1921
).interpret(s, DummyPath("(memory)"))
2022
}
2123

22-
def eval(s: String, preserveOrder: Boolean = false, strict: Boolean = false, noDuplicateKeysInComprehension: Boolean = false) = {
23-
eval0(s, preserveOrder, strict, noDuplicateKeysInComprehension) match {
24+
def eval(s: String,
25+
preserveOrder: Boolean = false,
26+
strict: Boolean = false,
27+
noDuplicateKeysInComprehension: Boolean = false,
28+
strictInheritedAssertions: Boolean = false) = {
29+
eval0(s, preserveOrder, strict, noDuplicateKeysInComprehension, strictInheritedAssertions) match {
2430
case Right(x) => x
2531
case Left(e) => throw new Exception(e)
2632
}
2733
}
2834

29-
def evalErr(s: String, preserveOrder: Boolean = false, strict: Boolean = false, noDuplicateKeysInComprehension: Boolean = false) = {
30-
eval0(s, preserveOrder, strict, noDuplicateKeysInComprehension) match{
35+
def evalErr(s: String,
36+
preserveOrder: Boolean = false,
37+
strict: Boolean = false,
38+
noDuplicateKeysInComprehension: Boolean = false,
39+
strictInheritedAssertions: Boolean = false) = {
40+
eval0(s, preserveOrder, strict, noDuplicateKeysInComprehension, strictInheritedAssertions) match{
3141
case Left(err) => err.split('\n').map(_.trim).mkString("\n") // normalize inconsistent indenation on JVM vs JS
3242
case Right(r) => throw new Exception(s"Expected exception, got result: $r")
3343
}

0 commit comments

Comments
 (0)