Skip to content

Commit

Permalink
Common isWellFormed() implementation (#3829)
Browse files Browse the repository at this point in the history
  • Loading branch information
xrtm000 authored Jul 17, 2023
1 parent 961e643 commit b773589
Show file tree
Hide file tree
Showing 7 changed files with 52 additions and 36 deletions.
8 changes: 2 additions & 6 deletions lang/js/src/main/scala/com/wavesplatform/lang/Global.scala
Original file line number Diff line number Diff line change
Expand Up @@ -120,9 +120,8 @@ object Global extends BaseGlobal {
if (value.toLong.toDouble == value && value - 1 < Long.MaxValue) BD.valueOf(value.toLong)
else BD.valueOf(value)

Right(BigInt(decimal
.setScale(scale.toInt, round.mode)
.unscaledValue))
val scaled = decimal.setScale(scale.toInt, round.mode).unscaledValue
Right(BigInt(scaled))
}

implicit class BigIntOps(val v: BigInteger) extends AnyVal {
Expand All @@ -143,7 +142,4 @@ object Global extends BaseGlobal {

override def ecrecover(messageHash: Array[Byte], signature: Array[Byte]): Array[Byte] =
???

override def isIllFormed(s: String): Boolean =
false
}
25 changes: 9 additions & 16 deletions lang/jvm/src/main/scala/com/wavesplatform/lang/Global.scala
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
package com.wavesplatform.lang

import java.math.{BigInteger, MathContext, BigDecimal => BD}
import java.security.spec.InvalidKeySpecException

import cats.syntax.either._
import cats.syntax.either.*
import ch.obermuhlner.math.big.BigDecimalMath
import com.google.common.base.Utf8
import com.google.common.io.BaseEncoding
import com.wavesplatform.common.merkle.Merkle
import com.wavesplatform.common.utils.{Base58, Base64}
Expand All @@ -14,11 +10,13 @@ import com.wavesplatform.lang.v1.BaseGlobal
import com.wavesplatform.lang.v1.evaluator.ctx.impl.Rounding
import com.wavesplatform.lang.v1.evaluator.ctx.impl.crypto.RSA
import com.wavesplatform.lang.v1.evaluator.ctx.impl.crypto.RSA.DigestAlgorithm
import com.wavesplatform.zwaves.bls12.{Groth16 => Bls12Groth16}
import com.wavesplatform.zwaves.bn256.{Groth16 => Bn256Groth16}
import com.wavesplatform.zwaves.bls12.Groth16 as Bls12Groth16
import com.wavesplatform.zwaves.bn256.Groth16 as Bn256Groth16
import org.web3j.crypto.Sign
import org.web3j.crypto.Sign.SignatureData

import java.math.{BigInteger, MathContext, BigDecimal as BD}
import java.security.spec.InvalidKeySpecException
import scala.annotation.tailrec
import scala.util.Try

Expand Down Expand Up @@ -82,8 +80,8 @@ object Global extends BaseGlobal {
private val longContext = new MathContext(longDigits)
private val oldLongContext = MathContext.DECIMAL128

private val bigIntDigits = 154
private val bigMathContext = new MathContext(bigIntDigits)
private val bigIntDigits = 154
private val bigMathContext = new MathContext(bigIntDigits)
private val oldBigMathContext = new MathContext(156 + 40)

// Math functions
Expand Down Expand Up @@ -125,10 +123,8 @@ object Global extends BaseGlobal {

def powBigInt(b: BigInt, bp: Long, e: BigInt, ep: Long, rp: Long, round: Rounding, useNewPrecision: Boolean): Either[String, BigInt] =
tryEither {
val base = toJBig(b, bp)
val exp = toJBig(e, ep)


val base = toJBig(b, bp)
val exp = toJBig(e, ep)

val context = if (useNewPrecision) bigMathContext else oldBigMathContext
val res = if (exp == BigDecimal(0.5).bigDecimal) {
Expand Down Expand Up @@ -187,7 +183,4 @@ object Global extends BaseGlobal {
val pk = Sign.signedMessageHashToKey(messageHash, signatureData)
base16Encoder.decode(pk.toString(16))
}

override def isIllFormed(s: String): Boolean =
Try(Utf8.encodedLength(s)).isFailure
}
20 changes: 18 additions & 2 deletions lang/shared/src/main/scala/com/wavesplatform/lang/package.scala
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package com.wavesplatform

import cats.{Eval, Id, Monad, StackSafeMonad}
import cats.data.EitherT
import cats.{Eval, Id, Monad, StackSafeMonad}
import monix.eval.Coeval

package object lang {
implicit def toError(msg: String): CommonError = CommonError(msg)

type ExecutionLog = String
type ExecutionLog = String

type CoevalF[F[_], A] = Coeval[F[A]]
type EvalF[F[_], A] = Eval[F[A]]
Expand All @@ -20,4 +20,20 @@ package object lang {
override def pure[A](x: A): Coeval[A] =
Coeval.now(x)
}

implicit class StringOps(val s: String) extends AnyVal {
def isWellFormed: Boolean = {
var i = 0
var wellFormed = true
while (i < s.length && wellFormed) {
val c = s.charAt(i)
if (Character.isSurrogate(c)) {
if (s.codePointAt(i) == c) wellFormed = false
else i += 1
}
i += 1
}
wellFormed
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -385,8 +385,6 @@ trait BaseGlobal {
}
}
}

def isIllFormed(s: String): Boolean
}

object BaseGlobal {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package com.wavesplatform.lang.v1.compiler
import cats.implicits.*
import cats.{Id, Show}
import com.wavesplatform.common.state.ByteStr
import com.wavesplatform.lang.CommonError
import com.wavesplatform.lang.v1.compiler.CompilationError.*
import com.wavesplatform.lang.v1.compiler.CompilerContext.*
import com.wavesplatform.lang.v1.compiler.Terms.*
Expand All @@ -28,14 +27,13 @@ import com.wavesplatform.lang.v1.parser.Expressions.{
import com.wavesplatform.lang.v1.parser.Parser.LibrariesOffset
import com.wavesplatform.lang.v1.parser.{BinaryOperation, Expressions, Parser}
import com.wavesplatform.lang.v1.task.imports.*
import com.wavesplatform.lang.v1.{BaseGlobal, ContractLimits, FunctionHeader}
import com.wavesplatform.lang.v1.{ContractLimits, FunctionHeader}
import com.wavesplatform.lang.{CommonError, StringOps}

import java.nio.charset.StandardCharsets
import scala.util.Try

object ExpressionCompiler {
private val global: BaseGlobal = com.wavesplatform.lang.Global

case class CompilationStepResultExpr(
ctx: CompilerContext,
expr: Terms.EXPR,
Expand Down Expand Up @@ -126,7 +124,7 @@ object ExpressionCompiler {

def adjustStr(expr: Expressions.CONST_STRING, str: String): Either[CompilationError, CompilationStepResultExpr] =
CONST_STRING(str)
.filterOrElse(_ => allowIllFormedStrings || !global.isIllFormed(str), CommonError(s"String '$str' contains ill-formed characters"))
.filterOrElse(_ => allowIllFormedStrings || str.isWellFormed, CommonError(s"String '$str' contains ill-formed characters"))
.leftMap(e => CompilationError.Generic(expr.position.start, expr.position.end, e.message))
.map(CompilationStepResultExpr(ctx, _, STRING, expr))
.recover { case err => CompilationStepResultExpr(ctx, FAILED_EXPR(), NOTHING, expr, List(err)) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1023,7 +1023,7 @@ object PureContext {
case CONST_STRING(m) :: CONST_STRING(sub) :: Nil =>
Right {
val i = m.indexOf(sub)
if (!global.isIllFormed(sub) && i != -1)
if (sub.isWellFormed && i != -1)
CONST_LONG(m.codePointCount(0, i).toLong)
else
unit
Expand Down Expand Up @@ -1067,7 +1067,7 @@ object PureContext {
) {
case CONST_STRING(m) :: CONST_STRING(sub) :: CONST_LONG(off) :: Nil =>
val l = m.codePointCount(0, m.length)
Right(if (!global.isIllFormed(sub) && off >= 0 && off <= l) {
Right(if (sub.isWellFormed && off >= 0 && off <= l) {
val i = m.indexOf(sub, m.offsetByCodePoints(0, off.toInt))
if (i != -1) {
CONST_LONG(m.codePointCount(0, i).toLong)
Expand Down Expand Up @@ -1113,7 +1113,7 @@ object PureContext {
case CONST_STRING(m) :: CONST_STRING(sub) :: Nil =>
Right({
val i = m.lastIndexOf(sub)
if (!global.isIllFormed(sub) && i != -1) {
if (sub.isWellFormed && i != -1) {
CONST_LONG(m.codePointCount(0, i).toLong)
} else {
unit
Expand Down Expand Up @@ -1161,7 +1161,7 @@ object PureContext {
Right(if (off >= 0) {
val offset = Math.min(off, m.codePointCount(0, m.length)).toInt
val i = m.lastIndexOf(sub, m.offsetByCodePoints(0, offset))
if (!global.isIllFormed(sub) && i != -1) {
if (sub.isWellFormed && i != -1) {
CONST_LONG(m.codePointCount(0, i).toLong)
} else {
unit
Expand Down Expand Up @@ -1205,7 +1205,7 @@ object PureContext {

private def split(str: String, sep: String, unicode: Boolean): Iterable[CONST_STRING] = {
if (str == "") listWithEmptyStr
else if (unicode && global.isIllFormed(sep)) List(CONST_STRING(str).explicitGet())
else if (unicode && !sep.isWellFormed) List(CONST_STRING(str).explicitGet())
else if (sep == "")
if (unicode) {
(1 to str.codePointCount(0, str.length))
Expand Down
15 changes: 15 additions & 0 deletions lang/tests-js/src/test/scala/com/wavesplatform/JsAPITest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -222,5 +222,20 @@ object JsAPITest extends JsTestBase {
JSON.stringify(result.exprAst.expr) ==> JSON.stringify(JSON.parse(expected))
JSON.stringify(result.errorList) ==> "[]"
}

test("ill-formed characters") {
val script =
"""
|{-# STDLIB_VERSION 6 #-}
|{-# CONTENT_TYPE DAPP #-}
|{-# SCRIPT_TYPE ACCOUNT #-}
|
|func call(a: String, b: Int) = {
| let zzz = "aaa\ud87ebbb"
| ([], zzz)
|}
""".stripMargin
assertCompileError(script, "contains ill-formed characters")
}
}
}

0 comments on commit b773589

Please sign in to comment.