Skip to content

Commit

Permalink
Merge pull request #1026 from ergoplatform/i462
Browse files Browse the repository at this point in the history
[6.0] Global.some() and Global.none methods
  • Loading branch information
kushti authored Nov 27, 2024
2 parents fa4b4b4 + ee5fa99 commit f3e21b1
Show file tree
Hide file tree
Showing 12 changed files with 200 additions and 6 deletions.
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 @@ -795,5 +795,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 @@ -484,6 +484,12 @@ object ReflectionData {
},
mkMethod(clazz, "decodeNbits", Array[Class[_]](classOf[Long])) { (obj, args) =>
obj.asInstanceOf[SigmaDslBuilder].decodeNbits(args(0).asInstanceOf[Long])
},
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 @@ -1929,6 +1929,21 @@ case object SGlobalMethods extends MonoTypeMethods {
Colls.fromArray(w.toBytes)
}

lazy val someMethod = SMethod(this, "some",
SFunc(Array(SGlobal, tT), SOption(tT), Array(paramT)), 9, 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)), 10, 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(
Expand All @@ -1939,7 +1954,9 @@ case object SGlobalMethods extends MonoTypeMethods {
deserializeToMethod,
encodeNBitsMethod,
decodeNBitsMethod,
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 @@ -272,6 +272,14 @@ class CSigmaDslBuilder extends SigmaDslBuilder { dsl =>
val res = DataSerializer.deserialize(tpe, reader)
res.asInstanceOf[T]
}

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 @@ -1210,6 +1210,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 @@ -550,6 +550,12 @@ object GraphIRReflection {
},
mkMethod(clazz, "deserializeTo", Array[Class[_]](classOf[Base#Ref[_]], classOf[TypeDescs#Elem[_]])) { (obj, args) =>
obj.asInstanceOf[ctx.SigmaDslBuilder].deserializeTo(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 @@ -122,6 +122,8 @@ import scalan._
def serialize[T](value: Ref[T]): Ref[Coll[Byte]]
def fromBigEndianBytes[T](bytes: Ref[Coll[Byte]])(implicit cT: Elem[T]): Ref[T]
def deserializeTo[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 @@ -1999,8 +1999,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 @@ -2212,6 +2226,21 @@ object SigmaDslBuilder extends EntityObject("SigmaDslBuilder") {
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))))
}


override def encodeNbits(bi: Ref[BigInt]): Ref[Long] = {
asRep[Long](mkMethodCall(source,
SigmaDslBuilderClass.getMethod("encodeNbits", classOf[Sym]),
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 @@ -2158,4 +2158,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 @@ -519,7 +519,7 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit with C
(SGlobal.typeId, Seq(
MInfo(1, groupGeneratorMethod), MInfo(2, xorMethod)
) ++ (if (isV6Activated) {
Seq(MInfo(3, serializeMethod), MInfo(4, deserializeToMethod), MInfo(5, fromBigEndianBytesMethod), MInfo(6, encodeNBitsMethod), MInfo(7, decodeNBitsMethod), MInfo(8, powHitMethod)) // methods added in v6.0
Seq(MInfo(3, serializeMethod), MInfo(4, deserializeToMethod), MInfo(5, fromBigEndianBytesMethod), MInfo(6, encodeNBitsMethod), MInfo(7, decodeNBitsMethod), MInfo(8, powHitMethod), MInfo(9, someMethod), MInfo(10, 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 @@ -39,6 +39,7 @@ import sigma.interpreter.{ContextExtension, ProverResult}
import sigma.util.NBitsUtils
import sigma.serialization.{DataSerializer, ErgoTreeSerializer, SigmaByteWriter}
import sigma.util.Extensions
import sigma.validation.ValidationException
import sigmastate.utils.Helpers
import sigmastate.utils.Helpers._

Expand Down Expand Up @@ -2298,4 +2299,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()
}
}

}

0 comments on commit f3e21b1

Please sign in to comment.