Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[6.0] Global.some() and Global.none methods #1026

Open
wants to merge 8 commits into
base: v6.0.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions core/shared/src/main/scala/sigma/SigmaDsl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -778,5 +778,9 @@ trait SigmaDslBuilder {

/** Returns a number decoded from provided big-endian bytes array. */
def fromBigEndianBytes[T](bytes: Coll[Byte])(implicit cT: RType[T]): T

def some[T](value: T)(implicit cT: RType[T]): Option[T]

def none[T]()(implicit cT: RType[T]): Option[T]
}

Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,12 @@ object ReflectionData {
},
mkMethod(clazz, "fromBigEndianBytes", Array[Class[_]](cColl, classOf[RType[_]])) { (obj, args) =>
obj.asInstanceOf[SigmaDslBuilder].fromBigEndianBytes(args(0).asInstanceOf[Coll[Byte]])(args(1).asInstanceOf[RType[_]])
},
mkMethod(clazz, "some", Array[Class[_]](classOf[Object], classOf[RType[_]])) { (obj, args) =>
obj.asInstanceOf[SigmaDslBuilder].some(args(0).asInstanceOf[Any])(args(1).asInstanceOf[RType[Any]])
},
mkMethod(clazz, "none", Array[Class[_]](classOf[RType[_]])) { (obj, args) =>
obj.asInstanceOf[SigmaDslBuilder].none()(args(0).asInstanceOf[RType[_]])
}
)
)
Expand Down
19 changes: 18 additions & 1 deletion data/shared/src/main/scala/sigma/ast/methods.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1871,13 +1871,30 @@ case object SGlobalMethods extends MonoTypeMethods {
Colls.fromArray(w.toBytes)
}

lazy val someMethod = SMethod(this, "some",
SFunc(Array(SGlobal, tT), SOption(tT), Array(paramT)), 8, FixedCost(JitCost(5)), Seq(tT))
.withIRInfo(MethodCallIrBuilder,
javaMethodOf[SigmaDslBuilder, Any, RType[_]]("some"),
{ mtype => Array(mtype.tRange) })
.withInfo(MethodCall, "Wrap given input into optional value (Option()).",
ArgInfo("value", "Value to wrap into Option."))

lazy val noneMethod = SMethod(this, "none",
SFunc(Array(SGlobal), SOption(tT), Array(paramT)), 9, FixedCost(JitCost(5)), Seq(tT))
.withIRInfo(MethodCallIrBuilder,
javaMethodOf[SigmaDslBuilder, RType[_]]("none"),
{ mtype => Array(mtype.tRange) })
.withInfo(MethodCall, "Returns empty Option[T] of given type T.")

protected override def getMethods() = super.getMethods() ++ {
if (VersionContext.current.isV6SoftForkActivated) {
Seq(
groupGeneratorMethod,
xorMethod,
serializeMethod,
fromBigEndianBytesMethod
fromBigEndianBytesMethod,
someMethod,
noneMethod
)
} else {
Seq(
Expand Down
8 changes: 8 additions & 0 deletions data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,14 @@ class CSigmaDslBuilder extends SigmaDslBuilder { dsl =>
DataSerializer.serialize(value.asInstanceOf[SType#WrappedType], tpe, w)
Colls.fromArray(w.toBytes)
}

override def some[T](value: T)(implicit cT: RType[T]): Option[T] = {
Some(value)
}

override def none[T]()(implicit cT: RType[T]): Option[T] = {
None
}
}

/** Default singleton instance of Global object, which implements global ErgoTree functions. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ import sigma.serialization.CoreByteWriter.{ArgInfo, DataInfo}
import sigma.ast._
import sigma.ast.syntax.SValue
import SigmaByteWriter._
import debox.cfor
import sigma.util.safeNewArray

import scala.collection.compat.immutable.ArraySeq

case class PropertyCallSerializer(cons: (Value[SType], SMethod, IndexedSeq[Value[SType]], STypeSubst) => Value[SType])
extends ValueSerializer[MethodCall] {
Expand All @@ -17,14 +21,33 @@ case class PropertyCallSerializer(cons: (Value[SType], SMethod, IndexedSeq[Value
w.put(mc.method.objType.typeId, typeCodeInfo)
w.put(mc.method.methodId, methodCodeInfo)
w.putValue(mc.obj, objInfo)
mc.method.explicitTypeArgs.foreach { a =>
val tpe = mc.typeSubst(a) // existence is checked in MethodCall constructor
w.putType(tpe)
}
}

override def parse(r: SigmaByteReader): Value[SType] = {
val typeId = r.getByte()
val methodId = r.getByte()
val obj = r.getValue()
val method = SMethod.fromIds(typeId, methodId)
val specMethod = method.specializeFor(obj.tpe, SType.EmptySeq)
cons(obj, specMethod, Value.EmptySeq, EmptySubst)

val (explicitTypeSubst: Map[STypeVar, SType], specMethod: SMethod) = if (method.hasExplicitTypeArgs) {
val nTypes = method.explicitTypeArgs.length
val res = safeNewArray[SType](nTypes)
cfor(0)(_ < nTypes, _ + 1) { i =>
res(i) = r.getType()
}
val explicitTypes = ArraySeq.unsafeWrapArray(res)
val explicitTypeSubst = method.explicitTypeArgs.zip(explicitTypes).toMap
val specMethod = method.withConcreteTypes(explicitTypeSubst)
(explicitTypeSubst, specMethod)
} else {
val specMethod = method.specializeFor(obj.tpe, SType.EmptySeq)
(EmptySubst, specMethod)
}

cons(obj, specMethod, Value.EmptySeq, explicitTypeSubst)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1184,6 +1184,13 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext =>
val bytes = asRep[Coll[Byte]](argsV(0))
val cT = stypeToElem(method.stype.tRange.withSubstTypes(typeSubst))
g.fromBigEndianBytes(bytes)(cT)
case SGlobalMethods.someMethod.name =>
val value = asRep[tT.WrappedType](argsV(0))
val cT = stypeToElem(typeSubst.apply(tT)).asInstanceOf[Elem[tT.WrappedType]]
g.some(value)(cT)
case SGlobalMethods.noneMethod.name =>
val cT = stypeToElem(typeSubst.apply(tT)).asInstanceOf[Elem[tT.WrappedType]]
g.none()(cT)
case _ => throwError()
}
case (x: Ref[tNum], _: SNumericTypeMethods) => method.name match {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -533,6 +533,12 @@ object GraphIRReflection {
},
mkMethod(clazz, "fromBigEndianBytes", Array[Class[_]](classOf[Base#Ref[_]], classOf[TypeDescs#Elem[_]])) { (obj, args) =>
obj.asInstanceOf[ctx.SigmaDslBuilder].fromBigEndianBytes(args(0).asInstanceOf[ctx.Ref[ctx.Coll[Byte]]])(args(1).asInstanceOf[ctx.Elem[SType]])
},
mkMethod(clazz, "some", Array[Class[_]](classOf[Base#Ref[_]], classOf[TypeDescs#Elem[_]])) { (obj, args) =>
obj.asInstanceOf[ctx.SigmaDslBuilder].some(args(0).asInstanceOf[ctx.Ref[Any]])(args(1).asInstanceOf[ctx.Elem[Any]])
},
mkMethod(clazz, "none", Array[Class[_]](classOf[TypeDescs#Elem[_]])) { (obj, args) =>
obj.asInstanceOf[ctx.SigmaDslBuilder].none()(args(0).asInstanceOf[ctx.Elem[SType]])
}
)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@ import scalan._
def xor(l: Ref[Coll[Byte]], r: Ref[Coll[Byte]]): Ref[Coll[Byte]]
def serialize[T](value: Ref[T]): Ref[Coll[Byte]]
def fromBigEndianBytes[T](bytes: Ref[Coll[Byte]])(implicit cT: Elem[T]): Ref[T]
def some[T](value: Ref[T])(implicit cT: Elem[T]): Ref[WOption[T]]
def none[T]()(implicit cT: Elem[T]): Ref[WOption[T]]
};
trait CostModelCompanion;
trait BigIntCompanion;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1992,8 +1992,22 @@ object SigmaDslBuilder extends EntityObject("SigmaDslBuilder") {
override def fromBigEndianBytes[T](bytes: Ref[Coll[Byte]])(implicit cT: Elem[T]): Ref[T] = {
asRep[T](mkMethodCall(self,
SigmaDslBuilderClass.getMethod("fromBigEndianBytes", classOf[Sym], classOf[Elem[T]]),
Array[AnyRef](bytes, cT, Map(tT -> Evaluation.rtypeToSType(cT.sourceType))),
true, false, cT))
Array[AnyRef](bytes, cT),
true, false, cT, Map(tT -> Evaluation.rtypeToSType(cT.sourceType))))
}

override def some[T](value: Ref[T])(implicit cT: Elem[T]): Ref[WOption[T]] = {
asRep[WOption[T]](mkMethodCall(self,
SigmaDslBuilderClass.getMethod("some", classOf[Sym], classOf[Elem[T]]),
Array[AnyRef](value, cT),
true, false, element[WOption[T]], Map(tT -> Evaluation.rtypeToSType(cT.sourceType))))
}

override def none[T]()(implicit cT: Elem[T]): Ref[WOption[T]] = {
asRep[WOption[T]](mkMethodCall(self,
SigmaDslBuilderClass.getMethod("none", classOf[Elem[T]]),
Array[AnyRef](cT),
true, false, element[WOption[T]], Map(tT -> Evaluation.rtypeToSType(cT.sourceType))))
}

}
Expand Down Expand Up @@ -2169,6 +2183,21 @@ object SigmaDslBuilder extends EntityObject("SigmaDslBuilder") {
Array[AnyRef](bytes, cT),
true, true, cT, Map(tT -> Evaluation.rtypeToSType(cT.sourceType))))
}

def some[T](value: Ref[T])(implicit cT: Elem[T]): Ref[WOption[T]] = {
asRep[WOption[T]](mkMethodCall(source,
SigmaDslBuilderClass.getMethod("some", classOf[Sym], classOf[Elem[T]]),
Array[AnyRef](value, cT),
true, true, element[WOption[T]], Map(tT -> Evaluation.rtypeToSType(cT.sourceType))))
}

def none[T]()(implicit cT: Elem[T]): Ref[WOption[T]] = {
asRep[WOption[T]](mkMethodCall(source,
SigmaDslBuilderClass.getMethod("none", classOf[Elem[T]]),
Array[AnyRef](cT),
true, true, element[WOption[T]], Map(tT -> Evaluation.rtypeToSType(cT.sourceType))))
}

}

// entityUnref: single unref method for each type family
Expand Down
26 changes: 26 additions & 0 deletions sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1950,4 +1950,30 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite =>
)
}

property("Global.some") {
lazy val some = newFeature(
{ (x: Byte) => CSigmaDslBuilder.some[Byte](x) },
"{ (x: Byte) => Global.some[Byte](x) }",
sinceVersion = V6SoftForkVersion)
val cases = Seq(
(0.toByte, Success(Some(0.toByte))),
(1.toByte, Success(Some(1.toByte)))
)

testCases(cases, some)
}

property("Global.none") {
lazy val some = newFeature(
{ (x: Byte) => CSigmaDslBuilder.none[Byte]() },
"{ (x: Byte) => Global.none[Byte]() }",
sinceVersion = V6SoftForkVersion)
val cases = Seq(
(0.toByte, Success(None)),
(1.toByte, Success(None))
)

testCases(cases, some)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -520,7 +520,7 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit with C
MInfo(1, groupGeneratorMethod), MInfo(2, xorMethod)
) ++ (if (isV6Activated) {
// id = 4 reserved for deserializeTo method
Seq(MInfo(3, serializeMethod), MInfo(5, fromBigEndianBytesMethod)) // methods added in v6.0
Seq(MInfo(3, serializeMethod), MInfo(5, fromBigEndianBytesMethod), MInfo(8, someMethod), MInfo(9, noneMethod)) // methods added in v6.0
} else {
Seq.empty[MInfo]
}), true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import sigma.eval.EvalSettings
import sigma.exceptions.InvalidType
import sigma.serialization.ErgoTreeSerializer
import sigma.interpreter.{ContextExtension, ProverResult}
import sigma.validation.ValidationException
import sigmastate.utils.Helpers
import sigmastate.utils.Helpers._

Expand Down Expand Up @@ -1080,7 +1081,6 @@ class BasicOpsSpecification extends CompilerTestingCommons
}
}

// todo: failing, needs for Header (de)serialization support from https://github.com/ScorexFoundation/sigmastate-interpreter/pull/972
property("serialize - collection of collection of headers") {
val td = new SigmaTestingData {}
val h1 = td.TestData.h1
Expand Down Expand Up @@ -1888,4 +1888,69 @@ class BasicOpsSpecification extends CompilerTestingCommons
)
}

property("Global.some") {
val ext: Seq[VarBinding] = Seq(
(intVar1, IntConstant(0))
)
def someTest(): Assertion = {
test("some", env, ext,
"""{
| val xo = Global.some[Int](5)
| xo.get == 5
|}""".stripMargin,
null
)
}

if (VersionContext.current.isV6SoftForkActivated) {
someTest()
} else {
an[sigma.validation.ValidationException] should be thrownBy someTest()
}
}

property("Global.some - computable value") {
val ext: Seq[VarBinding] = Seq(
(intVar1, IntConstant(0))
)
def someTest(): Assertion = {
test("some", env, ext,
"""{
| val i = getVar[Int](1)
| val xo = Global.some[Int](i.get)
| xo == i
|}""".stripMargin,
null
)
}

if (VersionContext.current.isV6SoftForkActivated) {
someTest()
} else {
an[sigma.validation.ValidationException] should be thrownBy someTest()
}
}

property("Global.none") {
val ext: Seq[VarBinding] = Seq(
(intVar1, IntConstant(0))
)
def someTest(): Assertion = {
test("some", env, ext,
"""{
| val xo = Global.some[Long](5L)
| val xn = Global.none[Long]()
| xn.isDefined == false && xn != xo
|}""".stripMargin,
null
)
}

if (VersionContext.current.isV6SoftForkActivated) {
someTest()
} else {
an[sigma.validation.ValidationException] should be thrownBy someTest()
}
}

}
Loading