Skip to content

Commit 89c04a0

Browse files
Add std.native & move std.xz/gzip to this (#241)
- Add support std.native - Move our non-standard std.xz/std.gzip to this. - Add gzip support to scala native.
1 parent 10709eb commit 89c04a0

File tree

8 files changed

+101
-37
lines changed

8 files changed

+101
-37
lines changed

sjsonnet/src-native/sjsonnet/Platform.scala

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,26 @@
11
package sjsonnet
2-
import java.io.File
2+
3+
import java.io.{ByteArrayOutputStream, File}
4+
import java.util.Base64
5+
import java.util.zip.GZIPOutputStream
6+
37
object Platform {
4-
def gzipBytes(s: Array[Byte]): String = {
5-
throw new Exception("GZip not implemented in Scala Native")
8+
def gzipBytes(b: Array[Byte]): String = {
9+
val outputStream: ByteArrayOutputStream = new ByteArrayOutputStream(b.length)
10+
val gzip: GZIPOutputStream = new GZIPOutputStream(outputStream)
11+
try {
12+
gzip.write(b)
13+
} finally {
14+
gzip.close()
15+
outputStream.close()
16+
}
17+
Base64.getEncoder.encodeToString(outputStream.toByteArray)
618
}
19+
720
def gzipString(s: String): String = {
8-
throw new Exception("GZip not implemented in Scala Native")
21+
gzipBytes(s.getBytes())
922
}
23+
1024
def xzBytes(s: Array[Byte], compressionLevel: Option[Int]): String = {
1125
throw new Exception("XZ not implemented in Scala Native")
1226
}

sjsonnet/src/sjsonnet/ReadWriter.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,8 @@ object ReadWriter{
4444
def apply(t: Val) = t.asFunc
4545
def write(pos: Position, t: Val.Func) = t
4646
}
47+
implicit object BuiltinRead extends ReadWriter[Val.Builtin] {
48+
def apply(t: Val) = t.asInstanceOf[Val.Builtin]
49+
def write(pos: Position, t: Val.Builtin) = t
50+
}
4751
}

sjsonnet/src/sjsonnet/Std.scala

Lines changed: 38 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,39 @@ import scala.util.matching.Regex
1616
* in Scala code. Uses `builtin` and other helpers to handle the common wrapper
1717
* logic automatically
1818
*/
19-
class Std {
19+
class Std(private val additionalNativeFunctions: Map[String, Val.Builtin] = Map.empty) {
2020
private val dummyPos: Position = new Position(null, 0)
2121
private val emptyLazyArray = new Array[Lazy](0)
2222
private val leadingWhiteSpacePattern = Pattern.compile("^[ \t\n\f\r\u0085\u00A0']+")
2323
private val trailingWhiteSpacePattern = Pattern.compile("[ \t\n\f\r\u0085\u00A0']+$")
24+
private val oldNativeFunctions = Map(
25+
builtin("gzip", "v"){ (_, _, v: Val) =>
26+
v match{
27+
case Val.Str(_, value) => Platform.gzipString(value)
28+
case arr: Val.Arr => Platform.gzipBytes(arr.iterator.map(_.cast[Val.Num].value.toByte).toArray)
29+
case x => Error.fail("Cannot gzip encode " + x.prettyName)
30+
}
31+
},
32+
33+
builtinWithDefaults("xz", "v" -> null, "compressionLevel" -> Val.Null(dummyPos)){ (args, pos, ev) =>
34+
val compressionLevel: Option[Int] = args(1) match {
35+
case Val.Null(_) =>
36+
// Use default compression level if the user didn't set one
37+
None
38+
case Val.Num(_, n) =>
39+
Some(n.toInt)
40+
case x =>
41+
Error.fail("Cannot xz encode with compression level " + x.prettyName)
42+
}
43+
args(0) match {
44+
case Val.Str(_, value) => Platform.xzString(value, compressionLevel)
45+
case arr: Val.Arr => Platform.xzBytes(arr.iterator.map(_.cast[Val.Num].value.toByte).toArray, compressionLevel)
46+
case x => Error.fail("Cannot xz encode " + x.prettyName)
47+
}
48+
},
49+
)
50+
require(oldNativeFunctions.forall(k => !additionalNativeFunctions.contains(k._1)), "Conflicting native functions")
51+
private val nativeFunctions = oldNativeFunctions ++ additionalNativeFunctions
2452

2553
private object AssertEqual extends Val.Builtin2("assertEqual", "a", "b") {
2654
def evalRhs(v1: Val, v2: Val, ev: EvalScope, pos: Position): Val = {
@@ -1285,31 +1313,6 @@ class Std {
12851313
new Val.Arr(pos, Base64.getDecoder().decode(s).map(i => Val.Num(pos, i)))
12861314
},
12871315

1288-
builtin("gzip", "v"){ (pos, ev, v: Val) =>
1289-
v match{
1290-
case Val.Str(_, value) => Platform.gzipString(value)
1291-
case arr: Val.Arr => Platform.gzipBytes(arr.iterator.map(_.cast[Val.Num].value.toByte).toArray)
1292-
case x => Error.fail("Cannot gzip encode " + x.prettyName)
1293-
}
1294-
},
1295-
1296-
builtinWithDefaults("xz", "v" -> null, "compressionLevel" -> Val.Null(dummyPos)){ (args, pos, ev) =>
1297-
val compressionLevel: Option[Int] = args(1) match {
1298-
case Val.Null(_) =>
1299-
// Use default compression level if the user didn't set one
1300-
None
1301-
case Val.Num(_, n) =>
1302-
Some(n.toInt)
1303-
case x =>
1304-
Error.fail("Cannot xz encode with compression level " + x.prettyName)
1305-
}
1306-
args(0) match {
1307-
case Val.Str(_, value) => Platform.xzString(value, compressionLevel)
1308-
case arr: Val.Arr => Platform.xzBytes(arr.iterator.map(_.cast[Val.Num].value.toByte).toArray, compressionLevel)
1309-
case x => Error.fail("Cannot xz encode " + x.prettyName)
1310-
}
1311-
},
1312-
13131316
builtin(EncodeUTF8),
13141317
builtin(DecodeUTF8),
13151318

@@ -1511,8 +1514,15 @@ class Std {
15111514
builtin(MaxArray),
15121515
builtin("primitiveEquals", "x", "y") { (_, ev, x: Val, y: Val) =>
15131516
x.isInstanceOf[y.type] && ev.compare(x, y) == 0
1514-
}
1515-
)
1517+
},
1518+
builtin("native", "name") { (pos, ev, name: String) =>
1519+
if (nativeFunctions.contains(name)) {
1520+
nativeFunctions(name)
1521+
} else {
1522+
Error.fail("Native function " + name + " not found", pos)(ev)
1523+
}
1524+
},
1525+
) ++ oldNativeFunctions
15161526

15171527
private def toSetArrOrString(args: Array[Val], idx: Int, pos: Position, ev: EvalScope) = {
15181528
args(idx) match {

sjsonnet/test/src-jvm-native/ErrorTests.scala

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -259,9 +259,8 @@ object ErrorTests extends TestSuite{
259259
)
260260
}
261261
test("native_not_found") - check(
262-
"""sjsonnet.Error: Field does not exist: native
263-
| at [Select native].(sjsonnet/test/resources/test_suite/error.native_not_found.jsonnet:17:4)
264-
| at [Apply1].(sjsonnet/test/resources/test_suite/error.native_not_found.jsonnet:17:11)
262+
"""sjsonnet.Error: Native function non_existent_native not found
263+
| at [std.native].(sjsonnet/test/resources/test_suite/error.native_not_found.jsonnet:17:11)
265264
|""".stripMargin
266265
)
267266
test("obj_assert") - {

sjsonnet/test/src-jvm/sjsonnet/Example.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package sjsonnet;
22

3+
import scala.collection.immutable.Map$;
4+
35
public class Example {
46
public void example(){
57
sjsonnet.SjsonnetMain.main0(
@@ -11,7 +13,7 @@ public void example(){
1113
os.package$.MODULE$.pwd(),
1214
scala.None$.empty(),
1315
scala.None$.empty(),
14-
new sjsonnet.Std().Std()
16+
new sjsonnet.Std(Map$.MODULE$.empty()).Std()
1517
);
1618
}
1719
}

sjsonnet/test/src-jvm/sjsonnet/StdGzipTests.scala

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,16 @@ object StdGzipTests extends TestSuite {
1616
case s if s >= 16 => "H4sIAAAAAAAA/8vIBACsKpPYAgAAAA=="
1717
case _ => "H4sIAAAAAAAAAMvIBACsKpPYAgAAAA=="
1818
})
19+
eval("""std.native('gzip')([1, 2])""") ==> ujson.Str(Runtime.version().feature() match {
20+
// https://bugs.openjdk.org/browse/JDK-8244706
21+
case s if s >= 16 => "H4sIAAAAAAAA/2NkAgCSQsy2AgAAAA=="
22+
case _ => "H4sIAAAAAAAAAGNkAgCSQsy2AgAAAA=="
23+
})
24+
eval("""std.native('gzip')("hi")""") ==> ujson.Str(Runtime.version().feature() match {
25+
// https://bugs.openjdk.org/browse/JDK-8244706
26+
case s if s >= 16 => "H4sIAAAAAAAA/8vIBACsKpPYAgAAAA=="
27+
case _ => "H4sIAAAAAAAAAMvIBACsKpPYAgAAAA=="
28+
})
1929
}
2030
}
2131
}

sjsonnet/test/src-jvm/sjsonnet/StdXzTests.scala

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,20 @@ object StdXzTests extends TestSuite {
1010
eval("""std.xz("hi")""") ==> ujson.Str("/Td6WFoAAATm1rRGAgAhARYAAAB0L+WjAQABaGkAAAD+qTgRvMqlSAABGgLcLqV+H7bzfQEAAAAABFla")
1111
eval("""std.xz([1, 2], compressionLevel = 0)""") ==> ujson.Str("/Td6WFoAAATm1rRGAgAhAQwAAACPmEGcAQABAQIAAADRC9qlUgJ94gABGgLcLqV+H7bzfQEAAAAABFla")
1212
eval("""std.xz("hi", compressionLevel = 1)""") ==> ujson.Str("/Td6WFoAAATm1rRGAgAhARAAAACocI6GAQABaGkAAAD+qTgRvMqlSAABGgLcLqV+H7bzfQEAAAAABFla")
13-
val ex = intercept[Exception] {
13+
var ex = intercept[Exception] {
1414
// Compression level 10 is invalid
1515
eval("""std.xz("hi", 10)""")
1616
}
1717
assert(ex.getMessage.contains("Unsupported preset: 10"))
18+
eval("""std.native('xz')([1, 2])""") ==> ujson.Str("/Td6WFoAAATm1rRGAgAhARYAAAB0L+WjAQABAQIAAADRC9qlUgJ94gABGgLcLqV+H7bzfQEAAAAABFla")
19+
eval("""std.native('xz')("hi")""") ==> ujson.Str("/Td6WFoAAATm1rRGAgAhARYAAAB0L+WjAQABaGkAAAD+qTgRvMqlSAABGgLcLqV+H7bzfQEAAAAABFla")
20+
eval("""std.native('xz')([1, 2], compressionLevel = 0)""") ==> ujson.Str("/Td6WFoAAATm1rRGAgAhAQwAAACPmEGcAQABAQIAAADRC9qlUgJ94gABGgLcLqV+H7bzfQEAAAAABFla")
21+
eval("""std.native('xz')("hi", compressionLevel = 1)""") ==> ujson.Str("/Td6WFoAAATm1rRGAgAhARAAAACocI6GAQABaGkAAAD+qTgRvMqlSAABGgLcLqV+H7bzfQEAAAAABFla")
22+
ex = intercept[Exception] {
23+
// Compression level 10 is invalid
24+
eval("""std.native('xz')("hi", 10)""")
25+
}
26+
assert(ex.getMessage.contains("Unsupported preset: 10"))
1827
}
1928
}
2029
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package sjsonnet
2+
3+
import sjsonnet.TestUtils.eval
4+
import utest._
5+
6+
object StdGzipTests extends TestSuite {
7+
val tests = Tests {
8+
test("gzip"){
9+
eval("""std.gzip([1, 2])""") ==> ujson.Str("H4sIAAAAAAAAAGNkAgCSQsy2AgAAAA==")
10+
eval("""std.gzip("hi")""") ==> ujson.Str("H4sIAAAAAAAAAMvIBACsKpPYAgAAAA==")
11+
eval("""std.native('gzip')([1, 2])""") ==> ujson.Str("H4sIAAAAAAAAAGNkAgCSQsy2AgAAAA==")
12+
eval("""std.native('gzip')("hi")""") ==> ujson.Str("H4sIAAAAAAAAAMvIBACsKpPYAgAAAA==")
13+
}
14+
}
15+
}
16+

0 commit comments

Comments
 (0)