diff --git a/.travis.yml b/.travis.yml index 471377a78af..9e5e2050c4f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,7 +10,7 @@ env: - secure: NYdeuFBQVVjN5gon2KQEDfNfyp9Bk6AZJ7V1tfLJW54y5Dd/4PHdWGWeOyhCMIqwX3UaUmWExY1KcKXpbDMLwTmahNLjiDc3e3rCqxghL5cDgqs6OHcwNGw+CwNiw3tufo5PZ9mhK/ZJh0qgOHWyETs/ifq4VyacvgYH9IZzK4YdKYdwf+T9c3Q7VO3KmzOXiHkvPvLfkc8I8cSZlT1kJFCGawRdE/0nygPd8HYfCxiEFuXOKpRz0hlm8njjZdlZpMcou8TLeH5fFSvHtELIHFvSTEj/QilSTmCXgFRVUd8sd53n3uTouwtVbdCHsXNm4Nphf0lUJnN7fTzbD2xpOZN9zS+SO/YTkIxZpSgQLwsJsWzIHI+6IOlcjeNwZHbYBfsAdv1c4GMkgFfxsWWtb4r7+ooXDTbyy1qeZgVU4caGBMwoNE+l0Y60JdGYT4hrTfJ5c+GpcPzIV3CYVOUkHmoCYXRCtxnfDFXCR0mDVfYAA14osLi3v+EKdTxHDfqP6fICIW9y7oZd//38iEQrDy+k9iCyNmvOsqvDxXZithmSP0WzeGCXQDI+s3FxB5ywt8yDvzFbF5JYY74o1tnNFG7TlSF5v8EHSHxzXBeBtnE9V1g2wxYnvW2HYk5kW6WZJgYE9LCc6ubpQ2W387PRfCJjoLNjHqNlNGkEYlkPuaw= - secure: rvBDWIiECsp1rI5wUjtw0098Gb2qE4r1uj33w+OtEnszftoZ+XgvvyyyfeykkcAAWob56pOFLox6CO1t/t3YyIyi8sXCPrf1eLBaFt4p6K5czNkWyWY6rlgd/cEXrJeE63UZhx9trN+jiZVjZ7oPpVNSNVAqXhf7dCHWOsKKu7i2gclzGYG1JeBL4eES/WJNVlgcsOdXbWG/udz9A/3+26afgN8Y5N/UMc0PJEmWUKcX7xWfLIcg4UIIvyPp5P55CT698N1jz57GHESDyYrMxMpZOO+0Q26Rjm+7A0U2+mm9XFE7cXT1JNFrJbUs/mvOpjTe0pScT34hRpH1pJcU3vppG/ffJ3IXB9/L2UfWcSemx4pNpdHuvt8Q2otdIlHxZrZpvHAOi6m/HkNBQlNYdRu9hjMVEmJUxiPtLl7npeMfbranEZ+gUtZoFV64aNBV6SUAmsi2supmj9xKmt3R0XOoMCR5zRglm/2EQY2fGhhp+OPrIWgSbGSTXXcUc5hEVM7sdTHo3taIbZsWya6NfjVJ/cxpBjc9hGpuSITU+r0WfSgTLqvEI3cgXQki/ggqVnDM80ft32KIUKIUaoaZSI7r5REps4A1F7HAP9+6VEJRk1p70Ls2IonzDifLij2LQDds1q8LbA9qOQtdj9beAGPvgKPJnBbH4rjNG5k+Iyw= script: - - sbt -S-Xfatal-warnings ";clean;set scalafmtOnCompile in ThisBuild := false;set scalafmtTestOnCompile in ThisBuild := true;coverage;generator/compile;langJS/compile;lang/test;test;coverageReport" + - sbt -S-Xfatal-warnings ";clean;set scalafmtOnCompile in ThisBuild := false;set scalafmtTestOnCompile in ThisBuild := true;coverage;generator/compile;langJS/fastOptJS;lang/test;test;coverageReport" after_success: - "[[ $TRAVIS_REPO_SLUG == \"wavesplatform/Scorex\" ]] && [[ $TRAVIS_BRANCH == \"master\" ]] && { sbt publish; };" - bash <(curl -s https://codecov.io/bash) diff --git a/lang/jvm/src/test/scala/com/wavesplatform/lang/EvaluatorTest.scala b/lang/jvm/src/test/scala/com/wavesplatform/lang/EvaluatorTest.scala index 80afb224aa3..c23dcae7fca 100644 --- a/lang/jvm/src/test/scala/com/wavesplatform/lang/EvaluatorTest.scala +++ b/lang/jvm/src/test/scala/com/wavesplatform/lang/EvaluatorTest.scala @@ -4,15 +4,16 @@ import cats.data.EitherT import com.wavesplatform.lang.Common._ import com.wavesplatform.lang.Terms.Typed._ import com.wavesplatform.lang.Terms._ +import com.wavesplatform.lang.TypeInfo._ import com.wavesplatform.lang.ctx._ import com.wavesplatform.lang.testing.ScriptGen import org.scalatest.prop.PropertyChecks import org.scalatest.{Matchers, PropSpec} -import scala.reflect.runtime.universe.TypeTag + class EvaluatorTest extends PropSpec with PropertyChecks with Matchers with ScriptGen with NoShrink { - private def ev[T: TypeTag](context: Context = Context.empty, expr: EXPR): Either[_, _] = Evaluator[T](context, expr) - private def simpleDeclarationAndUsage(i: Int) = BLOCK(Some(LET("x", CONST_LONG(i))), REF("x", LONG), LONG) + private def ev[T: TypeInfo](context: Context = Context.empty, expr: EXPR): Either[_, _] = Evaluator[T](context, expr) + private def simpleDeclarationAndUsage(i: Int) = BLOCK(Some(LET("x", CONST_LONG(i))), REF("x", LONG), LONG) property("successful on very deep expressions (stack overflow check)") { val term = (1 to 100000).foldLeft[EXPR](CONST_LONG(0))((acc, _) => BINARY_OP(acc, SUM_OP, CONST_LONG(1), LONG)) diff --git a/lang/jvm/src/test/scala/com/wavesplatform/lang/IntegrationTest.scala b/lang/jvm/src/test/scala/com/wavesplatform/lang/IntegrationTest.scala index 2a8739d4991..96e1981a1ad 100644 --- a/lang/jvm/src/test/scala/com/wavesplatform/lang/IntegrationTest.scala +++ b/lang/jvm/src/test/scala/com/wavesplatform/lang/IntegrationTest.scala @@ -1,14 +1,14 @@ package com.wavesplatform.lang -import org.scalatest.prop.PropertyChecks -import org.scalatest.{Matchers, PropSpec} import com.wavesplatform.lang.Common._ +import com.wavesplatform.lang.TypeInfo._ import com.wavesplatform.lang.ctx._ -import scala.reflect.runtime.universe.TypeTag +import org.scalatest.prop.PropertyChecks +import org.scalatest.{Matchers, PropSpec} class IntegrationTest extends PropSpec with PropertyChecks with Matchers with NoShrink { - private def eval[T: TypeTag](code: String) = { + private def eval[T: TypeInfo](code: String) = { val untyped = Parser(code).get.value val ctx = Context(Map.empty, Map.empty, Map(multiplierFunction.name -> multiplierFunction)) val typed = TypeChecker(TypeChecker.TypeCheckerContext.fromContext(ctx), untyped) diff --git a/lang/shared/src/main/scala/com/wavesplatform/lang/Evaluator.scala b/lang/shared/src/main/scala/com/wavesplatform/lang/Evaluator.scala index 510aea000b8..d38d0dbe92d 100644 --- a/lang/shared/src/main/scala/com/wavesplatform/lang/Evaluator.scala +++ b/lang/shared/src/main/scala/com/wavesplatform/lang/Evaluator.scala @@ -2,21 +2,20 @@ package com.wavesplatform.lang import cats.data.EitherT import com.wavesplatform.lang.Terms._ +import com.wavesplatform.lang.TypeInfo._ import com.wavesplatform.lang.ctx._ import monix.eval.Coeval -import scala.reflect.runtime.universe.TypeTag - import scala.util.{Failure, Success, Try} object Evaluator { - private def r[T: TypeTag](ctx: Context, t: TrampolinedExecResult[Typed.EXPR]): TrampolinedExecResult[T] = + private def r[T: TypeInfo](ctx: Context, t: TrampolinedExecResult[Typed.EXPR]): TrampolinedExecResult[T] = t flatMap { (typedExpr: Typed.EXPR) => (typedExpr match { case Typed.BLOCK(mayBeLet, inner, blockTpe) => mayBeLet match { - case None => r(ctx, EitherT.pure(inner))(blockTpe.typetag) + case None => r(ctx, EitherT.pure(inner))(blockTpe.typeInfo) case Some(Typed.LET(newVarName, newVarBlock)) => (ctx.letDefs.get(newVarName), ctx.functions.get(newVarName)) match { case (Some(_), _) => EitherT.leftT[Coeval, T](s"Value '$newVarName' already defined in the scope") @@ -24,10 +23,10 @@ object Evaluator { EitherT.leftT[Coeval, Typed.EXPR](s"Value '$newVarName' can't be defined because function with such name is predefined") case (None, None) => val varBlockTpe = newVarBlock.tpe - val eitherTCoeval: TrampolinedExecResult[varBlockTpe.Underlying] = r(ctx, EitherT.pure(newVarBlock))(varBlockTpe.typetag) + val eitherTCoeval: TrampolinedExecResult[varBlockTpe.Underlying] = r(ctx, EitherT.pure(newVarBlock))(varBlockTpe.typeInfo) val lz: LazyVal = LazyVal(varBlockTpe)(eitherTCoeval) val updatedCtx: Context = ctx.copy(letDefs = ctx.letDefs.updated(newVarName, lz)) - r(updatedCtx, EitherT.pure(inner))(blockTpe.typetag) + r(updatedCtx, EitherT.pure(inner))(blockTpe.typeInfo) } } case Typed.REF(str, _) => @@ -61,8 +60,8 @@ object Evaluator { case Typed.IF(cond, t1, t2, tpe) => r[Boolean](ctx, EitherT.pure(cond)) flatMap { - case true => r(ctx, EitherT.pure(t1))(tpe.typetag) - case false => r(ctx, EitherT.pure(t2))(tpe.typetag) + case true => r(ctx, EitherT.pure(t1))(tpe.typeInfo) + case false => r(ctx, EitherT.pure(t2))(tpe.typeInfo) } case Typed.BINARY_OP(t1, AND_OP, t2, BOOLEAN) => @@ -78,9 +77,8 @@ object Evaluator { case Typed.BINARY_OP(it1, EQ_OP, it2, tpe) => for { - i1 <- r(ctx, EitherT.pure(it1))(it1.tpe.typetag) - i2 <- r(ctx, EitherT.pure(it2))(it2.tpe.typetag) - + i1 <- r(ctx, EitherT.pure(it1))(it1.tpe.typeInfo) + i2 <- r(ctx, EitherT.pure(it2))(it2.tpe.typeInfo) } yield i1 == i2 case Typed.GETTER(expr, field, _) => r[Obj](ctx, EitherT.pure(expr)).flatMap { (obj: Obj) => @@ -98,7 +96,7 @@ object Evaluator { case Some(func) => val argsVector = args .map(a => - r(ctx, EitherT.pure(a))(a.tpe.typetag) + r(ctx, EitherT.pure(a))(a.tpe.typeInfo) .map(_.asInstanceOf[Any])) .toVector val argsSequenced = argsVector.sequence[TrampolinedExecResult, Any] @@ -113,15 +111,15 @@ object Evaluator { case Typed.BINARY_OP(_, AND_OP | OR_OP, _, tpe) if tpe != BOOLEAN => EitherT.leftT[Coeval, Any](s"Expected BOOLEAN, but got $tpe: $t") }).map { v => - val tt = implicitly[TypeTag[T]] + val ti = typeInfo[T] v match { - case x if typedExpr.tpe.typetag.tpe <:< tt.tpe => x.asInstanceOf[T] - case _ => throw new Exception(s"Bad type: expected: ${tt.tpe} actual: ${typedExpr.tpe.typetag.tpe}") + case x if typedExpr.tpe.typeInfo <:< ti => x.asInstanceOf[T] + case _ => throw new Exception(s"Bad type: expected: ${ti} actual: ${typedExpr.tpe.typeInfo}") } } } - def apply[A: TypeTag](c: Context, expr: Typed.EXPR): Either[ExecutionError, A] = { + def apply[A: TypeInfo](c: Context, expr: Typed.EXPR): Either[ExecutionError, A] = { def result = r[A](c, EitherT.pure(expr)).value.apply() Try(result) match { case Failure(ex) => Left(ex.toString) diff --git a/lang/shared/src/main/scala/com/wavesplatform/lang/Terms.scala b/lang/shared/src/main/scala/com/wavesplatform/lang/Terms.scala index 5b5843381cd..614d11def10 100644 --- a/lang/shared/src/main/scala/com/wavesplatform/lang/Terms.scala +++ b/lang/shared/src/main/scala/com/wavesplatform/lang/Terms.scala @@ -2,8 +2,6 @@ package com.wavesplatform.lang import com.wavesplatform.lang.ctx.Obj import scodec.bits.ByteVector -import scala.reflect.runtime.universe._ - object Terms { case class FUNCTION(args: List[TYPEPLACEHOLDER], result: TYPEPLACEHOLDER) @@ -14,11 +12,12 @@ object Terms { sealed trait TYPE extends TYPEPLACEHOLDER { type Underlying - def typetag: TypeTag[Underlying] + def typeInfo: TypeInfo[Underlying] } - sealed abstract class AUTO_TAGGED_TYPE[T](implicit override val typetag: TypeTag[T]) extends TYPE { + sealed abstract class AUTO_TAGGED_TYPE[T](implicit override val typeInfo: TypeInfo[T]) extends TYPE { override type Underlying = T } + case object NOTHING extends AUTO_TAGGED_TYPE[Nothing] case object UNIT extends AUTO_TAGGED_TYPE[Unit] case object LONG extends AUTO_TAGGED_TYPE[Long] @@ -27,7 +26,7 @@ object Terms { case object STRING extends AUTO_TAGGED_TYPE[String] case class OPTION(innerType: TYPE) extends TYPE { type Underlying = Option[innerType.Underlying] - override def typetag: TypeTag[Option[innerType.Underlying]] = typeTag[Underlying] + override def typeInfo: TypeInfo[Option[innerType.Underlying]] = TypeInfo.optionTypeInfo(innerType.typeInfo) } case class TYPEREF(name: String) extends AUTO_TAGGED_TYPE[Obj] diff --git a/lang/shared/src/main/scala/com/wavesplatform/lang/TypeInfo.scala b/lang/shared/src/main/scala/com/wavesplatform/lang/TypeInfo.scala new file mode 100644 index 00000000000..c9679661b6f --- /dev/null +++ b/lang/shared/src/main/scala/com/wavesplatform/lang/TypeInfo.scala @@ -0,0 +1,93 @@ +package com.wavesplatform.lang + +import cats.implicits._ +import com.wavesplatform.lang.ctx.Obj +import scodec.bits.ByteVector + +import scala.reflect.ClassTag + +final case class TypeInfo[T](classTag: ClassTag[T], + superClass: Option[TypeInfo[_]], + typeParams: Set[TypeInfo[_]], + interfaces: Set[TypeInfo[_]]) { self => + def <:< (other: TypeInfo[_]) = { + def loop(left: Set[TypeInfo[_]], seen: Set[TypeInfo[_]]): Boolean = { + left.nonEmpty && { + val next = left.head + val supers = next.interfaces ++ next.superClass + supers(other) || { + val xs = left ++ supers filterNot seen + loop(xs - next, seen + next) + } + } + } + + if (self.classTag.runtimeClass == other.classTag.runtimeClass) { + self.typeParams == other.typeParams + } else loop(Set(self), Set.empty) + } +} + +object TypeInfo { + def typeInfo[T](implicit ti: TypeInfo[T]): TypeInfo[T] = ti + + private def fromAny[A: ClassTag](typeParams: Set[TypeInfo[_]] = Set.empty, interfaces: Set[TypeInfo[_]] = Set.empty): TypeInfo[A] = { + TypeInfo(reflect.classTag[A], anyTypeInfo.some, typeParams, interfaces) + } + + private def fromAnyVal[A: ClassTag](typeParams: Set[TypeInfo[_]] = Set.empty, interfaces: Set[TypeInfo[_]] = Set.empty): TypeInfo[A] = { + TypeInfo(reflect.classTag[A], anyValTypeInfo.some, typeParams, interfaces) + } + + implicit val anyTypeInfo: TypeInfo[Any] = { + val tag = reflect.classTag[Any] + TypeInfo(tag, None, Set.empty, Set.empty) + } + + implicit val equalsTypeInfo: TypeInfo[Equals] = + fromAny() + + implicit val serializableTypeInfo: TypeInfo[Serializable] = + fromAny() + + implicit val productTypeInfo: TypeInfo[Product] = + fromAny(interfaces = Set(serializableTypeInfo)) + + implicit val anyValTypeInfo: TypeInfo[AnyVal] = + fromAny() + + implicit val nothingTypeInfo: TypeInfo[Nothing] = { + val tag = reflect.classTag[Nothing] + TypeInfo[Nothing]( + tag, + None, + Set.empty, + Set.empty + ) + } + + implicit val unitTypeInfo: TypeInfo[Unit] = + fromAnyVal() + implicit val longTypeInfo: TypeInfo[Long] = + fromAnyVal() + + /** + * BitwiseOperations also should be in interfaces, + * but i dont know how to create TypeInfo with + * recursive type parameters. + */ + implicit val byteVectorTypeInfo: TypeInfo[ByteVector] = + fromAny(interfaces = Set(serializableTypeInfo)) + + implicit val booleanTypeInfo: TypeInfo[Boolean] = + fromAnyVal() + + implicit val stringTypeInfo: TypeInfo[String] = + fromAnyVal(interfaces = Set(serializableTypeInfo)) + + implicit val objTypeInfo: TypeInfo[Obj] = + fromAny(interfaces = Set(productTypeInfo, serializableTypeInfo)) + + implicit def optionTypeInfo[A](implicit tia: TypeInfo[A]): TypeInfo[Option[A]] = + fromAny(Set(tia), Set(productTypeInfo, serializableTypeInfo)) +} diff --git a/src/test/scala/com/wavesplatform/state2/diffs/smart/predef/CommonFunctionsTest.scala b/src/test/scala/com/wavesplatform/state2/diffs/smart/predef/CommonFunctionsTest.scala index 1b928b4bf48..e17cecd1c85 100644 --- a/src/test/scala/com/wavesplatform/state2/diffs/smart/predef/CommonFunctionsTest.scala +++ b/src/test/scala/com/wavesplatform/state2/diffs/smart/predef/CommonFunctionsTest.scala @@ -1,5 +1,6 @@ package com.wavesplatform.state2.diffs.smart.predef +import com.wavesplatform.lang.TypeInfo._ import com.wavesplatform.state2._ import com.wavesplatform.state2.diffs._ import com.wavesplatform.{NoShrink, TransactionGen} diff --git a/src/test/scala/com/wavesplatform/state2/diffs/smart/predef/package.scala b/src/test/scala/com/wavesplatform/state2/diffs/smart/predef/package.scala index b9af443fc2d..ec21105fb95 100644 --- a/src/test/scala/com/wavesplatform/state2/diffs/smart/predef/package.scala +++ b/src/test/scala/com/wavesplatform/state2/diffs/smart/predef/package.scala @@ -1,15 +1,14 @@ package com.wavesplatform.state2.diffs.smart -import com.wavesplatform.lang.Evaluator +import com.wavesplatform.lang.{Evaluator, TypeInfo} import com.wavesplatform.utils.dummyTypeCheckerContext import fastparse.core.Parsed.Success import monix.eval.Coeval import scorex.transaction.Transaction import scorex.transaction.smart.BlockchainContext -import scala.reflect.runtime.universe.TypeTag package object predef { val networkByte: Byte = 'u' - def runScript[T: TypeTag](script: String, tx: Transaction = null): Either[String, T] = { + def runScript[T: TypeInfo](script: String, tx: Transaction = null): Either[String, T] = { val Success(expr, _) = com.wavesplatform.lang.Parser(script) val Right(typedExpr) = com.wavesplatform.lang.TypeChecker(dummyTypeCheckerContext, expr) Evaluator[T](new BlockchainContext(networkByte, Coeval(tx), Coeval(???), null).build(), typedExpr) diff --git a/src/test/scala/com/wavesplatform/state2/diffs/smart/scenarios/HackatonScenartioTest.scala b/src/test/scala/com/wavesplatform/state2/diffs/smart/scenarios/HackatonScenartioTest.scala index 91218c86459..8aa1ff62880 100644 --- a/src/test/scala/com/wavesplatform/state2/diffs/smart/scenarios/HackatonScenartioTest.scala +++ b/src/test/scala/com/wavesplatform/state2/diffs/smart/scenarios/HackatonScenartioTest.scala @@ -2,21 +2,20 @@ package com.wavesplatform.state2.diffs.smart.scenarios import java.nio.charset.StandardCharsets -import com.wavesplatform.{NoShrink, TransactionGen} -import com.wavesplatform.lang.{Evaluator, Parser, TypeChecker} +import com.wavesplatform.lang.TypeInfo._ +import com.wavesplatform.lang.{Evaluator, Parser, TypeChecker, TypeInfo} import com.wavesplatform.state2._ import com.wavesplatform.state2.diffs._ import com.wavesplatform.state2.diffs.smart._ import com.wavesplatform.utils._ - -import scala.reflect.runtime.universe.TypeTag +import com.wavesplatform.{NoShrink, TransactionGen} import org.scalacheck.Gen -import org.scalatest.{Matchers, PropSpec} import org.scalatest.prop.PropertyChecks +import org.scalatest.{Matchers, PropSpec} import scorex.account.AddressScheme -import scorex.transaction.{DataTransaction, GenesisTransaction} import scorex.transaction.assets.{SmartIssueTransaction, TransferTransaction} import scorex.transaction.smart.Script +import scorex.transaction.{DataTransaction, GenesisTransaction} class HackatonScenartioTest extends PropSpec with PropertyChecks with Matchers with TransactionGen with NoShrink { val preconditions: Gen[ @@ -101,7 +100,7 @@ class HackatonScenartioTest extends PropSpec with PropertyChecks with Matchers w accountBDataTransaction, transferFromAToB) - private def eval[T: TypeTag](code: String) = { + private def eval[T: TypeInfo](code: String) = { val untyped = Parser(code).get.value val typed = TypeChecker(dummyTypeCheckerContext, untyped) typed.flatMap(Evaluator[T](dummyContext, _))