All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.wavesplatform.lang.API.scala Maven / Gradle / Ivy

package com.wavesplatform.lang

import cats.implicits.toBifunctorOps
import com.wavesplatform.lang.contract.meta.FunctionSignatures
import com.wavesplatform.lang.directives.Directive.extractDirectives
import com.wavesplatform.lang.directives.values.{Call, Expression, Library, StdLibVersion, DApp as DAppType}
import com.wavesplatform.lang.directives.{DirectiveParser, DirectiveSet}
import com.wavesplatform.lang.script.ScriptPreprocessor
import com.wavesplatform.lang.v1.BaseGlobal
import com.wavesplatform.lang.v1.BaseGlobal.DAppInfo
import com.wavesplatform.lang.v1.compiler.Terms.EXPR
import com.wavesplatform.lang.v1.compiler.{CompilationError, Types, UtilityFunctionPrefix}
import com.wavesplatform.lang.v1.estimator.ScriptEstimator
import com.wavesplatform.lang.v1.evaluator.ctx.FunctionTypeSignature
import com.wavesplatform.lang.v1.parser.Expressions
import com.wavesplatform.lang.v1.parser.Parser.LibrariesOffset

sealed trait CompileAndParseResult

object CompileAndParseResult {

  case class Expression(bytes: Array[Byte], complexity: Long, expr: Expressions.SCRIPT, errors: Seq[CompilationError]) extends CompileAndParseResult

  case class Library(bytes: Array[Byte], complexity: Long, expr: EXPR) extends CompileAndParseResult

  case class Contract(
      bytes: Array[Byte],
      verifierComplexity: Long,
      callableComplexities: Map[String, Long],
      expr: Expressions.DAPP,
      errors: Seq[CompilationError]
  ) extends CompileAndParseResult
}

sealed trait CompileResult {
  val version: StdLibVersion
  def bytes: Array[Byte]
  def verifierComplexity: Long
  def callableComplexities: Map[String, Long]
  def maxComplexity: Long
}
object CompileResult {
  case class Expression(version: StdLibVersion, bytes: Array[Byte], maxComplexity: Long, expr: EXPR, error: Either[String, Unit], isFreeCall: Boolean)
      extends CompileResult {
    override val callableComplexities: Map[String, Long] = Map.empty
    override val verifierComplexity: Long                = if (isFreeCall) 0 else maxComplexity
  }

  case class Library(version: StdLibVersion, bytes: Array[Byte], complexity: Long, expr: EXPR) extends CompileResult {
    override val verifierComplexity: Long                = 0
    override val callableComplexities: Map[String, Long] = Map.empty
    override val maxComplexity: Long                     = complexity
  }

  case class DApp(version: StdLibVersion, dAppInfo: DAppInfo, meta: FunctionSignatures, error: Either[String, Unit]) extends CompileResult {
    override def bytes: Array[Byte]                      = dAppInfo.bytes
    override def verifierComplexity: Long                = dAppInfo.verifierComplexity
    override def callableComplexities: Map[String, Long] = dAppInfo.callableComplexities
    override val maxComplexity: Long                     = callableComplexities.values.maxOption.fold(verifierComplexity)(_.max(verifierComplexity))
  }
}

object API {
  private val G: BaseGlobal                       = Global
  private val allEstimators: Seq[ScriptEstimator] = ScriptEstimator.all(fixOverflow = true)

  def latestEstimatorVersion: Int = allEstimators.length

  def allTypes(ver: Int = 2, isTokenContext: Boolean = false, isContract: Boolean = false): Seq[Types.FINAL] =
    utils.ctx(ver, isTokenContext, isContract).types

  def allVars(ver: Int = 2, isTokenContext: Boolean = false, isContract: Boolean = false): Seq[(String, Types.FINAL)] =
    utils
      .ctx(ver, isTokenContext, isContract)
      .vars
      .map { case (name, (t, _)) => (name, t) }
      .toSeq

  def allFunctions(ver: Int = 2, isTokenContext: Boolean = false, isContract: Boolean = false): Seq[(String, Seq[String], FunctionTypeSignature)] =
    utils
      .ctx(ver, isTokenContext, isContract)
      .functions
      .collect {
        case f if !f.name.startsWith(UtilityFunctionPrefix) => (f.name, f.args, f.signature)
      }
      .toSeq

  def parseAndCompile(
      input: String,
      estimatorVersion: Int = latestEstimatorVersion,
      needCompaction: Boolean = false,
      removeUnusedCode: Boolean = false,
      libraries: Map[String, String] = Map.empty
  ): Either[String, CompileAndParseResult] =
    for {
      estimatorVer <- Either.cond(
        estimatorVersion > 0 && estimatorVersion <= API.allEstimators.length,
        estimatorVersion,
        s"Version of estimator must be not greater than ${API.allEstimators.length}"
      )
      directives            <- DirectiveParser(input)
      ds                    <- extractDirectives(directives)
      (linkedInput, offset) <- ScriptPreprocessor(input, libraries, ds.imports)
      compiled <- parseAndCompileScript(ds, linkedInput, offset, API.allEstimators.toIndexedSeq(estimatorVer - 1), needCompaction, removeUnusedCode)
    } yield compiled

  private def parseAndCompileScript(
      ds: DirectiveSet,
      input: String,
      offset: LibrariesOffset,
      estimator: ScriptEstimator,
      needCompaction: Boolean,
      removeUnusedCode: Boolean
  ): Either[String, CompileAndParseResult] = {
    val stdLibVer = ds.stdLibVersion
    ds.contentType match {
      case Expression =>
        parseAndCompileExpression(ds, input, offset, estimator, stdLibVer)
      case Library =>
        parseAndCompileExpression(ds, input + "\ntrue", offset, estimator, stdLibVer)
      case DAppType =>
        G.parseAndCompileContract(
          input,
          offset,
          utils.compilerContext(ds),
          stdLibVer,
          estimator,
          needCompaction,
          removeUnusedCode
        ).map { case (bytes, (verifierComplexity, callableComplexities), dapp, errors) =>
          CompileAndParseResult.Contract(bytes, verifierComplexity, callableComplexities, dapp, errors.toSeq)
        }
    }
  }

  private def parseAndCompileExpression(
      ds: DirectiveSet,
      input: String,
      offset: LibrariesOffset,
      estimator: ScriptEstimator,
      stdLibVer: StdLibVersion
  ): Either[String, CompileAndParseResult.Expression] =
    G.parseAndCompileExpression(
      input,
      offset,
      utils.compilerContext(ds),
      G.LetBlockVersions.contains(stdLibVer),
      stdLibVer,
      estimator
    ).map { case (bytes, complexity, exprScript, errors) =>
      CompileAndParseResult.Expression(bytes, complexity, exprScript, errors.toSeq)
    }

  def compile(
      input: String,
      estimator: ScriptEstimator,
      needCompaction: Boolean = false,
      removeUnusedCode: Boolean = false,
      libraries: Map[String, String] = Map.empty,
      defaultStdLib: StdLibVersion = StdLibVersion.VersionDic.default,
      allowFreeCall: Boolean = true
  ): Either[String, CompileResult] =
    for {
      directives            <- DirectiveParser(input)
      ds                    <- extractDirectives(directives, defaultStdLib)
      (linkedInput, offset) <- ScriptPreprocessor(input, libraries, ds.imports)
      compiled              <- compileScript(ds, linkedInput, offset, estimator, needCompaction, removeUnusedCode, allowFreeCall)
    } yield compiled

  def estimatorByVersion(version: Int): Either[String, ScriptEstimator] =
    Either
      .cond(
        version > 0 && version <= allEstimators.length,
        version,
        s"Version of estimator must be not greater than ${API.allEstimators.length}"
      )
      .map(v => allEstimators.toIndexedSeq(v - 1))

  private def compileScript(
      ds: DirectiveSet,
      input: String,
      offset: LibrariesOffset,
      estimator: ScriptEstimator,
      needCompaction: Boolean,
      removeUnusedCode: Boolean,
      allowFreeCall: Boolean
  ): Either[String, CompileResult] = {
    val version = ds.stdLibVersion
    val ctx     = utils.compilerContext(ds)
    (ds.contentType, ds.scriptType) match {
      case (Expression, Call) if allowFreeCall =>
        G.compileFreeCall(input, offset, ctx, version, ds.scriptType, estimator)
          .map { case (bytes, expr, complexity) =>
            val check = G.checkExpr(expr, complexity, version, ds.scriptType, estimator)
            CompileResult.Expression(version, bytes, complexity, expr, check, isFreeCall = true)
          }
      case (Expression, Call) =>
        Left("Invoke Expression Transaction is not activated yet")
      case (Expression, _) =>
        G.compileExpression(input, offset, ctx, version, ds.scriptType, estimator)
          .map { case (bytes, expr, complexity) =>
            val check = G.checkExpr(expr, complexity, version, ds.scriptType, estimator)
            CompileResult.Expression(version, bytes, complexity, expr, check, isFreeCall = false)
          }
      case (Library, _) =>
        G.compileDecls(input, offset, ctx, version, ds.scriptType, estimator)
          .map { case (bytes, expr, complexity) =>
            CompileResult.Library(version, bytes, complexity, expr)
          }
      case (DAppType, _) =>
        // Just ignore stdlib version here
        G.compileContract(input, offset, ctx, version, estimator, needCompaction, removeUnusedCode)
          .flatMap { di =>
            val check = G.checkContract(version, di.dApp, di.maxComplexity, di.annotatedComplexities, estimator)
            G.dAppFuncTypes(di.dApp).bimap(_.m, CompileResult.DApp(version, di, _, check))
          }
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy