Skip to content

Commit

Permalink
NODE-2569 Fix compaction for scripts with global variables and functi…
Browse files Browse the repository at this point in the history
…ons (#3826)
  • Loading branch information
ivan-mashonskiy authored Mar 24, 2023
1 parent aa00aff commit 2619abc
Show file tree
Hide file tree
Showing 36 changed files with 1,053 additions and 852 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.wavesplatform.lang.v1.compiler
import com.wavesplatform.lang.contract.DApp
import com.wavesplatform.lang.v1.FunctionHeader.{Native, User}
import com.wavesplatform.lang.v1.compiler.Terms.{BLOCK, DECLARATION, EXPR, FUNC, FUNCTION_CALL, GETTER, IF, LET, LET_BLOCK, REF}
import com.wavesplatform.lang.v1.evaluator.ctx.impl.GlobalValNames

import scala.annotation.tailrec

Expand All @@ -13,7 +14,7 @@ object ContractScriptCompactor {
type ReplaceNameF = (String, State) => CompactionResult[String]

def compact(dApp: DApp): DApp =
compact(dApp, State(0, Map.empty))
compact(dApp, State(0, Map.empty, Set.empty))

def decompact(dApp: DApp): DApp =
if (dApp.meta.originalNames.nonEmpty) {
Expand Down Expand Up @@ -69,10 +70,10 @@ object ContractScriptCompactor {
case None =>
val compactName = idxToName(state.counter)
if (hasConflict(compactName, dApp)) {
createCompName(oldName, State(state.counter + 1, state.originalNames), dApp)
createCompName(oldName, State(state.counter + 1, state.originalNames, state.knownDecs), dApp)
} else {
assert(!hasConflict(compactName, dApp))
CompactionResult(compactName, State(state.counter + 1, state.originalNames.updated(oldName, compactName)))
CompactionResult(compactName, State(state.counter + 1, state.originalNames.updated(oldName, compactName), state.knownDecs))
}
}

Expand All @@ -83,31 +84,29 @@ object ContractScriptCompactor {
state.originalNames.getOrElse(oldName, oldName)

private def decompact(dApp: DApp, originalNames: Vector[String]): DApp = {
val compNameToOriginalName = originalNames.zipWithIndex.map {
case (originalName, idx) =>
val compName = idxToName(idx)
val resultOriginalName = if (originalName.isEmpty) compName else originalName
compName -> resultOriginalName
val compNameToOriginalName = originalNames.zipWithIndex.map { case (originalName, idx) =>
val compName = idxToName(idx)
val resultOriginalName = if (originalName.isEmpty) compName else originalName
compName -> resultOriginalName
}.toMap

val compDAppRes = processDappWithoutMeta(dApp, State(0, compNameToOriginalName), restoreOriginalName)
val compDAppRes = processDappWithoutMeta(dApp, State(0, compNameToOriginalName, Set.empty), restoreOriginalName)
compDAppRes.value.copy(
meta = dApp.meta.withOriginalNames(Vector.empty)
)
}

private def decompactOld(dApp: DApp, originalNames: Map[String, String]): DApp = {
val compDAppRes = processDappWithoutMeta(dApp, State(0, originalNames), restoreOriginalName)
val compDAppRes = processDappWithoutMeta(dApp, State(0, originalNames, Set.empty), restoreOriginalName)
compDAppRes.value.copy(
meta = dApp.meta.withCompactNameAndOriginalNamePairList(Seq.empty)
)
}

private def compact(dApp: DApp, state: State): DApp = {
val compDAppRes = processDappWithoutMeta(dApp, state, createCompName(_, _, dApp))
val oldNameToIdx = compDAppRes.state.originalNames.map {
case (oldName, compName) =>
oldName -> nameToIdx(compName)
val oldNameToIdx = compDAppRes.state.originalNames.map { case (oldName, compName) =>
oldName -> nameToIdx(compName)
}
val emptyElemsWithIdx = oldNameToIdx.values.maxOption
.map { maxIdx =>
Expand All @@ -123,28 +122,34 @@ object ContractScriptCompactor {
}

private def hasConflict(compactName: String, dApp: DApp): Boolean =
dApp.callableFuncs.exists(_.u.name == compactName)
dApp.callableFuncs.exists(_.u.name == compactName) || GlobalValNames.All.contains(compactName)

private def processLet(let: LET, state: State, replaceNameF: ReplaceNameF): CompactionResult[LET] = {
val compNameRes = replaceNameF(let.name, state)
val compValueRes = processExpr(let.value, compNameRes.state, replaceNameF)

CompactionResult(let.copy(name = compNameRes.value, value = compValueRes.value), compValueRes.state)
CompactionResult(
let.copy(name = compNameRes.value, value = compValueRes.value),
compValueRes.state.copy(knownDecs = compNameRes.state.knownDecs + let.name)
)
}

private def processFunc(func: FUNC, state: State, replaceNameF: ReplaceNameF): CompactionResult[FUNC] = {
val compNameRes = replaceNameF(func.name, state)
val compArgsRes = processList(func.args, compNameRes.state, replaceNameF)
val compBodyRes = processExpr(func.body, compArgsRes.state, replaceNameF)
val compBodyRes = processExpr(func.body, compArgsRes.state.addKnownDecs(func.args), replaceNameF)

CompactionResult(func.copy(name = compNameRes.value, args = compArgsRes.value, body = compBodyRes.value), compBodyRes.state)
CompactionResult(
func.copy(name = compNameRes.value, args = compArgsRes.value, body = compBodyRes.value),
compBodyRes.state.copy(knownDecs = state.knownDecs + func.name)
)
}

private def processDec(dec: DECLARATION, state: State, replaceNameF: ReplaceNameF): CompactionResult[DECLARATION] = {
dec match {
case l: LET => processLet(l, state, replaceNameF)
case l: LET => processLet(l, state, replaceNameF)
case f: FUNC => processFunc(f, state, replaceNameF)
case other => CompactionResult(other, state)
case other => CompactionResult(other, state)
}
}

Expand All @@ -154,22 +159,30 @@ object ContractScriptCompactor {
val compDecRes = processDec(b.dec, state, replaceNameF)
val compBodyRes = processExpr(b.body, compDecRes.state, replaceNameF)

CompactionResult(b.copy(dec = compDecRes.value, body = compBodyRes.value), compBodyRes.state)
CompactionResult(b.copy(dec = compDecRes.value, body = compBodyRes.value), compBodyRes.state.copy(knownDecs = state.knownDecs))
case lb: LET_BLOCK =>
val compLetRes = processLet(lb.let, state, replaceNameF)
val compBodyRes = processExpr(lb.body, compLetRes.state, replaceNameF)

CompactionResult(lb.copy(let = compLetRes.value, body = compBodyRes.value), compBodyRes.state)
CompactionResult(lb.copy(let = compLetRes.value, body = compBodyRes.value), compBodyRes.state.copy(knownDecs = state.knownDecs))
case fc: FUNCTION_CALL =>
val newFunction = fc.function match {
case User(internalName, _) => User(getReplacedName(internalName, state))
case nF: Native => nF
case User(internalName, _) if state.knownDecs.contains(internalName) =>
User(getReplacedName(internalName, state))
case uF: User => uF
case nF: Native => nF
}
val compArgsRes = processList[EXPR](fc.args, state, processExpr(_, _, replaceNameF))

CompactionResult(fc.copy(function = newFunction, args = compArgsRes.value), compArgsRes.state)
case r: REF =>
CompactionResult(r.copy(key = getReplacedName(r.key, state)), state)
val newKey = if (state.knownDecs.contains(r.key)) {
getReplacedName(r.key, state)
} else {
r.key
}

CompactionResult(r.copy(key = newKey), state)
case g: GETTER =>
val compExprRes = processExpr(g.expr, state, replaceNameF)

Expand All @@ -191,24 +204,24 @@ object ContractScriptCompactor {
case (CompactionResult(compFuncs, state), func) =>
val compInvArgNameRes = replaceNameF(func.annotation.invocationArgName, state)
val compArgsRes = processList(func.u.args, compInvArgNameRes.state, replaceNameF)
val compBodyRes = processExpr(func.u.body, compArgsRes.state, replaceNameF)
val compBodyRes = processExpr(func.u.body, compArgsRes.state.addKnownDecs(func.annotation.invocationArgName +: func.u.args), replaceNameF)
val compFunc = func.copy(
annotation = func.annotation.copy(invocationArgName = compInvArgNameRes.value),
u = func.u.copy(args = compArgsRes.value, body = compBodyRes.value)
)

CompactionResult(compFuncs :+ compFunc, compBodyRes.state)
CompactionResult(compFuncs :+ compFunc, compBodyRes.state.copy(knownDecs = state.knownDecs))
}

val compVerifierFuncOptRes = dApp.verifierFuncOpt
.fold[CompactionResult[Option[DApp.VerifierFunction]]](CompactionResult(None, compCallableFuncsRes.state)) { vFunc =>
val compInvArgNameRes = replaceNameF(vFunc.annotation.invocationArgName, compCallableFuncsRes.state)
val compFuncRes = processFunc(vFunc.u, compInvArgNameRes.state, replaceNameF)
val compFuncRes = processFunc(vFunc.u, compInvArgNameRes.state.addKnownDecs(Seq(vFunc.annotation.invocationArgName)), replaceNameF)
val newVFunc = vFunc.copy(
annotation = vFunc.annotation.copy(invocationArgName = compInvArgNameRes.value),
u = compFuncRes.value
)
CompactionResult(Some(newVFunc), compFuncRes.state)
CompactionResult(Some(newVFunc), compFuncRes.state.copy(knownDecs = compInvArgNameRes.state.knownDecs))
}

CompactionResult(
Expand Down Expand Up @@ -249,6 +262,8 @@ object ContractScriptCompactor {
loop(list, compF, CompactionResult(Vector.empty[A], state))
}

case class State(counter: Int, originalNames: Map[String, String])
case class State(counter: Int, originalNames: Map[String, String], knownDecs: Set[String]) {
def addKnownDecs(dec: Seq[String]): State = copy(knownDecs = knownDecs ++ dec)
}
case class CompactionResult[+A](value: A, state: State)
}
Loading

0 comments on commit 2619abc

Please sign in to comment.