diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..f90d0053 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,13 @@ +.idea/ +codegen/target/ +common/target/ +dotnet/target/ +node/target/ +node-db/target/ +plaintest/target/ +testkit/target/ +vm/target/ +vm-api/target/ +vm-asm/target/ +yaml4s/target/ +yopt/target/ \ No newline at end of file diff --git a/.drone.yml b/.drone.yml index 77d56e4a..8ae2219d 100644 --- a/.drone.yml +++ b/.drone.yml @@ -6,6 +6,11 @@ workspace: base: /workdir path: code pipeline: + tag-the-image: + image: alpine/git:1.0.4 + pull: false + commands: + - echo -n `git describe --tags | sed -e "s/^v//"`",latest" > .tags tests: image: gcr.io/time-coin/sbt:latest pull: true @@ -16,7 +21,7 @@ pipeline: - export COURSIER_CACHE='/workdir/coursier/' - export SBT_OPTS='-Dsbt.global.base=/workdir/sbt/ -Dsbt.ivy.home=/workdir/ivy/ -Divy.home=/workdir/ivy/' - cd /workdir/code && sbt $SBT_OPTS -mem 2048 headerCheck scalafmtCheck test:scalafmtCheck test "gen-doc/run --check" - package-zip-and-publish-to-bintray: + package-zip: image: gcr.io/time-coin/sbt:latest volumes: - /var/lib/sbt-cache:/workdir/sbt @@ -24,8 +29,7 @@ pipeline: commands: - export COURSIER_CACHE='/workdir/coursier/' - export SBT_OPTS='-Dsbt.global.base=/workdir/sbt/ -Dsbt.ivy.home=/workdir/ivy/ -Divy.home=/workdir/ivy/' - - cd /workdir/code && sbt $SBT_OPTS -mem 2048 cli/universal:packageZipTarball cli/universal:stage node/docker:stage publish - secrets: [ bintray_user, bintray_pass ] + - cd /workdir/code && sbt $SBT_OPTS -mem 2048 cli/universal:packageZipTarball cli/universal:stage broadcaster/universal:stage faucet/universal:stage when: event: tag package-msi: @@ -38,18 +42,27 @@ pipeline: - mv /workdir/code/cli/target/universal/*.tgz /workdir/code/cli/target/universal/PravdaSDK-$$PRAVDA_VERSION.tgz when: event: tag - package-publish-nuget: - image: microsoft/dotnet:2.1-sdk - environment: - - PRAVDA_VERSION=${DRONE_TAG##v} + sign-msi: + image: byrnedo/alpine-curl commands: - - cd /workdir/code/PravdaDotNet - - dotnet pack -c Release - - dotnet nuget push bin/Release/Expload.Pravda.$PRAVDA_VERSION.nupkg -k $NUGET_KEY -s https://api.nuget.org/v3/index.json - secrets: [ nuget_key ] + - find . -name *.msi -exec curl -F msi=@{} http://35.246.239.138:8080/sign-msi -o {} \; + when: + event: tag + publish-to-bintray: + group: publish + image: gcr.io/time-coin/sbt:latest + volumes: + - /var/lib/sbt-cache:/workdir/sbt + - /var/lib/coursier-cache:/workdir/coursier + commands: + - export COURSIER_CACHE='/workdir/coursier/' + - export SBT_OPTS='-Dsbt.global.base=/workdir/sbt/ -Dsbt.ivy.home=/workdir/ivy/ -Divy.home=/workdir/ivy/' + - cd /workdir/code && sbt $SBT_OPTS -mem 2048 publish + secrets: [ bintray_user, bintray_pass ] when: event: tag publish-github-releases: + group: publish image: plugins/github-release files: - /workdir/code/win-installer/*.msi @@ -58,30 +71,53 @@ pipeline: when: event: tag publish-docker-hub: + group: publish image: plugins/docker repo: expload/pravda - context: /workdir/code/node/target/docker/stage/ - dockerfile: /workdir/code/node/target/docker/stage/Dockerfile + context: . + dockerfile: docker/images/pravda-cli/Dockerfile secrets: [ docker_username, docker_password ] - auto_tag: true when: event: tag - publish-docker-image-private: - image: plugins/gcr - registry: gcr.io/time-coin - repo: gcr.io/time-coin/expload-pravda-node + publish-pravda-faucet: + group: publish + image: plugins/docker + repo: expload/pravda-faucet context: . - dockerfile: ./docker/images/expload-pravda-node/Dockerfile - secrets: [ gcr_json_key ] + dockerfile: docker/images/pravda-faucet/Dockerfile + secrets: [ docker_username, docker_password ] when: - event: push - branch: master - publish-docker-image-private-tagged: - image: plugins/gcr - registry: gcr.io/time-coin - repo: gcr.io/time-coin/expload-pravda-node + event: tag + publish-broadcaster-to-docker: + group: publish + image: plugins/docker + repo: expload/pravda-broadcaster context: . - dockerfile: ./docker/images/expload-pravda-node/Dockerfile - secrets: [ gcr_json_key ] + dockerfile: docker/images/broadcaster/Dockerfile + secrets: [ docker_username, docker_password ] + when: + event: tag + publish-pravda-dll-nuget: + group: dotnet + image: microsoft/dotnet:2.2-sdk + environment: + - PRAVDA_VERSION=${DRONE_TAG##v} + commands: + - cd /workdir/code/PravdaDotNet + - dotnet pack -c Release + - dotnet nuget push bin/Release/Expload.Pravda.$PRAVDA_VERSION.nupkg -k $NUGET_KEY -s https://api.nuget.org/v3/index.json + secrets: [ nuget_key ] + when: + event: tag + publish-pravda-program-template-nuget: + group: dotnet + image: chicker/nuget:0.8 + environment: + - PRAVDA_VERSION=${DRONE_TAG##v} + commands: + - cd /workdir/code/PravdaProgramTemplate + - nuget pack -Version $PRAVDA_VERSION Expload.PravdaProgramTemplate.nuspec + - dotnet nuget push Expload.PravdaProgramTemplate.$PRAVDA_VERSION.nupkg -k $NUGET_KEY -s https://api.nuget.org/v3/index.json + secrets: [ nuget_key ] when: event: tag diff --git a/PravdaDotNet/Pravda.cs b/PravdaDotNet/Pravda.cs index 8a11be23..5a67c5b1 100644 --- a/PravdaDotNet/Pravda.cs +++ b/PravdaDotNet/Pravda.cs @@ -10,10 +10,26 @@ public class Program : Attribute {} // Access to the storage public class Mapping { - public V get(K key) { return default(V); } - public bool exists(K key) { return false; } - public void put(K key, V value) { return; } - public V getDefault(K key, V def) { return default(V); } + + public V this[K k] { + get { + return default(V); + } + set { + return; + } + } + + // Checks the specified key is present in the mapping. + public bool ContainsKey(K key) { + return false; + } + + // Gets the value associated with the specified key. + // If key is not fount returns defaultValue. + public V GetOrDefault(K key, V defaultValue) { + return default(V); + } } public class Info { @@ -25,6 +41,20 @@ public class Info { // Get program address public static Bytes ProgramAddress() { return null; } + + // Get list of contract's callers' addresses + public static Bytes[] Callers() { return null; } + + public static long Height() { return 0L; } + public static Bytes LastBlockHash() { return null; } + } + + public class Actions { + // Transfer native coins from executor account + public static void Transfer(Bytes to, long amount) { return; } + + // Transfer native coins from program account + public static void TransferFromProgram(Bytes to, long amount) { return; } } public class Log { @@ -42,11 +72,11 @@ public class Bytes { // Get the void address (32 zero bytes) public static Bytes VOID_ADDRESS = null; - public Bytes(params byte[] bytes) {} + public Bytes(params sbyte[] bytes) {} public Bytes(String hex) {} // Get the i-th byte - public byte this[int i] { get { return 0; } set { return; } } + public sbyte this[int i] { get { return 0; } } // Get the sub-array public Bytes Slice(int start, int length) { return null; } // Concatenate two Bytes @@ -60,6 +90,7 @@ public class StdLib { public static Bytes Ripemd160(String str) { return null; } public static Bytes HexToBytes(String hex) { return null; } + public static String BytesToHex(Bytes bytes) { return null; } public static bool ValidateEd25519Signature(Bytes pubKey, Bytes message, Bytes signature) { return false; } public static bool ValidateEd25519Signature(Bytes pubKey, String message, Bytes signature) { return false; } diff --git a/PravdaDotNet/Pravda.csproj b/PravdaDotNet/Pravda.csproj index 1d70845c..67ce9db8 100644 --- a/PravdaDotNet/Pravda.csproj +++ b/PravdaDotNet/Pravda.csproj @@ -3,5 +3,6 @@ Expload.Pravda $(PRAVDA_VERSION) netstandard2.0 + $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb diff --git a/PravdaDotNet/Pravda.dll b/PravdaDotNet/Pravda.dll index a0cc9587..4ab5b742 100644 Binary files a/PravdaDotNet/Pravda.dll and b/PravdaDotNet/Pravda.dll differ diff --git a/PravdaProgramTemplate/Content/.template.config/template.json b/PravdaProgramTemplate/Content/.template.config/template.json new file mode 100644 index 00000000..8e76c793 --- /dev/null +++ b/PravdaProgramTemplate/Content/.template.config/template.json @@ -0,0 +1,25 @@ +{ + "$schema": "http://json.schemastore.org/template", + "author": "Alexander Voronkov", + "classifications": [ "Common", "Console" ], + "identity": "Expload.PravdaProgramTemplate", + "name": "Pravda Program Template", + "shortName": "pravdaprogram", + "sourceName": "MyProgram", + "symbols": { + "namespace": { + "type": "parameter", + "datatype": "string", + "defaultValue": "MyCompany", + "replaces": "NAMESPACE", + "description": "Program namespace" + }, + "version": { + "type": "parameter", + "datatype": "string", + "defaultValue": "1.0.0", + "replaces": "VERSION", + "description": "Program version" + } + } +} diff --git a/PravdaProgramTemplate/Content/MyProgram.cs b/PravdaProgramTemplate/Content/MyProgram.cs new file mode 100644 index 00000000..6db62eff --- /dev/null +++ b/PravdaProgramTemplate/Content/MyProgram.cs @@ -0,0 +1,13 @@ +namespace NAMESPACE { + + using Expload.Pravda; + using System; + + [Program] + public class MyProgram { + + public string HelloWorld() { + return "Hello, world!"; + } + } +} diff --git a/PravdaProgramTemplate/Content/MyProgram.csproj b/PravdaProgramTemplate/Content/MyProgram.csproj new file mode 100644 index 00000000..f65cbd83 --- /dev/null +++ b/PravdaProgramTemplate/Content/MyProgram.csproj @@ -0,0 +1,26 @@ + + + MyProgram + NAMESPACE.$(PackageName) + VERSION + netstandard2.0 + $(PackageId) + 500000 + http://publicnode.expload.com/api/public + + + + + + + + + + + + + + + + + diff --git a/PravdaProgramTemplate/Content/README.md b/PravdaProgramTemplate/Content/README.md new file mode 100644 index 00000000..3be37e65 --- /dev/null +++ b/PravdaProgramTemplate/Content/README.md @@ -0,0 +1,39 @@ +# Pravda Program Template + +## Installing the template + +```bash +dotnet new -i Expload.PravdaProgramTemplate +dotnet new pravdaprogram +``` + +## Building and running + +First, you have to generate a wallet and put it into wallet.json (or move it to the folder if you have one). + +```bash +pravda gen address -o wallet.json +``` + +Then use [Faucet](http://faucet.dev.expload.com/ui) to get some XCoin. After you have funds on your wallet, deploy the program. + +```bash +dotnet publish -c Deploy +``` + +To update program run + +```bash +dotnet publish -c Update +``` + +## FAQ + +> I get `NotEnoughMoney` exception. What do I do? + +Make sure you've used faucet to get XCoin for account in `wallet.json`. + +> What do I do next? + +Now you can edit the program in *.cs and deploy scripts +in .csproj to suit your needs. Make sure to check out [Pravda docs](https://expload.com/developers/documentation/pravda/)! diff --git a/PravdaProgramTemplate/Expload.PravdaProgramTemplate.nuspec b/PravdaProgramTemplate/Expload.PravdaProgramTemplate.nuspec new file mode 100644 index 00000000..aa8b5f35 --- /dev/null +++ b/PravdaProgramTemplate/Expload.PravdaProgramTemplate.nuspec @@ -0,0 +1,12 @@ + + + + Expload.PravdaProgramTemplate + 1.0.0 + A template for Pravda Program + Alexander Voronkov + + + + + diff --git a/README.md b/README.md index 3a5e4140..3650b05b 100644 --- a/README.md +++ b/README.md @@ -4,28 +4,4 @@ Pravda is a general purpose blockchain with PoA consensus (PoW in future). It's Network node is build on top of [Tendermint](http://tendermint.com/), is written in Scala and runs on JVM. We have our own virtual machine for "smart contracts" (not exactly) and translators from [.NET](https://en.wikipedia.org/wiki/Common_Intermediate_Language) (and [EVM](https://ethereum.github.io/yellowpaper/paper.pdf) in future) bytecode. -## Documentation - -* [Getting Started](doc/getting-started.md) -* [Command line interface](doc/ref/cli/main.md) -* [Pravda Virtual Machine](doc/ref/vm) - * [Using assembler](doc/ref/vm/asm.md) - * [Internal data format](doc/ref/vm/data.md) - * [Opcodes](doc/ref/vm/opcodes.md) - * [Standard Library](doc/ref/vm/stdlib.md) - * [Meta information](doc/ref/vm/meta.md) -* [DApp API specification](doc/dapp-api.md) -* [Node API specification](doc/node-api.md) -* [Dotnet](doc/ref/dotnet) - * [Dotnet translation](doc/ref/dotnet/translation.md) - * [Dotnet classes translation](doc/ref/dotnet/classes-translation.md) -* [Code generation](doc/codegen.md) -* [Glossary](doc/glossary.md) -* [FAQ](doc/faq.md) - -## Development - - * [Participation](doc/dev/participation.md) - * [Building from sources](doc/dev/building-from-sources.md) - * [How to generate documentation](doc/dev/gen-doc.md) - * [How to add new functionality to VM](doc/dev/add-new-functionality.md) +[Documentation](https://expload.com/developers/documentation/pravda/) diff --git a/build.sbt b/build.sbt index 15a80443..5375f3d6 100644 --- a/build.sbt +++ b/build.sbt @@ -1,4 +1,5 @@ -import java.nio.file.Files +import java.nio.file.{Files, Paths} +import Dependencies._ resolvers += "jitpack" at "https://jitpack.io" resolvers += Resolver.bintrayRepo("expload", "oss") @@ -14,10 +15,22 @@ git.gitTagToVersionNumber := { tag: String => else None } -val `tendermint-version` = "0.16.0" +val `tendermint-version` = "0.26.4" + +lazy val envDockerUsername = sys.env.get("docker_username") + + +lazy val cleanupTestDirTask = TaskKey[Unit]("cleanupTestDirTask", "Cleanup test dir") + +cleanupTestDirTask := { + println("Cleaning up the temporary folder...") + sbt.IO.delete(Paths.get(System.getProperty("java.io.tmpdir"), "pravda").toFile) +} + +test in Test := (test in Test).dependsOn(cleanupTestDirTask).value val scalacheckOps = Seq( - libraryDependencies += "org.scalacheck" %% "scalacheck" % "1.14.0" % "test", + libraryDependencies += scalaCheck % "test", testOptions in Test ++= Seq( Tests.Argument(TestFrameworks.ScalaCheck, "-verbosity", "3"), Tests.Argument(TestFrameworks.ScalaCheck, "-workers", "1"), @@ -36,9 +49,9 @@ val commonSettings = Seq( bintrayVcsUrl := Some("https://github.com/expload/pravda"), libraryDependencies ++= Seq( // Tests - "org.typelevel" %% "cats-core" % "1.0.1", - "org.rudogma" %% "supertagged" % "1.4", - "com.lihaoyi" %% "utest" % "0.6.3" % "test" + catsCore, + superTagged, + uTest % "test" ), testFrameworks += new TestFramework("utest.runner.Framework"), scalacOptions ++= Seq( @@ -70,14 +83,14 @@ lazy val common = (project in file("common")) ) .settings( libraryDependencies ++= Seq( - "com.google.protobuf" % "protobuf-java" % "3.5.0", - "com.propensive" %% "contextual" % "1.1.0", - "org.whispersystems" % "curve25519-java" % "0.4.1", - "org.rudogma" %% "supertagged" % "1.4", - "com.tethys-json" %% "tethys" % "0.7.0.2", - "com.tethys-json" %% "tethys-derivation" % "0.7.0.2", - "com.tethys-json" %% "tethys-json4s" % "0.7.0.2", - "org.json4s" %% "json4s-ast" % "3.6.1" + protobufJava, + contextual, + curve25519Java, + superTagged, + tethys, + tethysDerivation, + tethysJson4s, + json4sAst ) ) @@ -94,9 +107,9 @@ lazy val `vm-api` = (project in file("vm-api")) Tests.Argument(TestFrameworks.ScalaCheck, "-minSuccessfulTests", "1000") ), libraryDependencies ++= Seq( - "com.google.protobuf" % "protobuf-java" % "3.5.0", - "com.lihaoyi" %% "fastparse" % "1.0.0", - "com.tethys-json" %% "tethys" % "0.7.0.2" + protobufJava, + fastParse, + tethys ) ) .dependsOn(common) @@ -109,10 +122,12 @@ lazy val vm = (project in file("vm")) description := "Pravda Virtual Machine", sources in doc := Seq.empty, publishArtifact in packageDoc := false, + libraryDependencies += "org.bouncycastle" % "bcprov-jdk15on" % "1.60" ) .settings( sources in doc := Seq.empty, publishArtifact in packageDoc := false, + testFrameworks := Seq(new TestFramework("pravda.common.PreserveColoursFramework")) ) .dependsOn(`vm-api`, `vm-asm` % "compile->test") .dependsOn(common % "compile->compile;test->test") @@ -142,7 +157,8 @@ lazy val evm = (project in file("evm")). settings( commonSettings: _* ). settings( libraryDependencies ++= Seq ( - "com.lihaoyi" %% "fastparse-byte" % "1.0.0" + "com.lihaoyi" %% "fastparse-byte" % "1.0.0", + "org.bouncycastle" % "bcprov-jdk15on" % "1.60" ) ) @@ -156,10 +172,12 @@ lazy val dotnet = (project in file("dotnet")) ) .settings( libraryDependencies ++= Seq( - "org.typelevel" %% "cats-core" % "1.0.1", - "com.lihaoyi" %% "fastparse-byte" % "1.0.0", - "com.lihaoyi" %% "pprint" % "0.5.3" % "test" - ) + catsCore, + fastParseByte, + pprint % "test", + commonsIo + ), + testFrameworks := Seq(new TestFramework("pravda.common.PreserveColoursFramework")) ) .dependsOn(`vm-asm`) .dependsOn(common % "test->test") @@ -175,42 +193,38 @@ lazy val `node-db` = (project in file("node-db")) ) .settings( normalizedName := "pravda-node-db", - libraryDependencies += "org.iq80.leveldb" % "leveldb" % "0.10" + libraryDependencies += levelDb ) lazy val node = (project in file("node")) .enablePlugins(UniversalPlugin) .enablePlugins(AshScriptPlugin) - .enablePlugins(DockerPlugin) + .enablePlugins(BuildInfoPlugin) .settings(commonSettings: _*) .settings(scalacheckOps:_*) .settings( - packageName in Docker := "pravda", - dockerExposedPorts := Seq(8080, 46656), - dockerUsername := Some("expload"), - dockerUpdateLatest := true, name := "pravda-node", normalizedName := "pravda-node", - description := "Pravda network node" + description := "Pravda network node", + buildInfoKeys := Seq[BuildInfoKey](version), + buildInfoPackage := "pravda.node", ) .settings( normalizedName := "pravda-node", libraryDependencies ++= Seq( // Networking - "com.typesafe.akka" %% "akka-actor" % "2.5.8", - "com.typesafe.akka" %% "akka-stream" % "2.5.8", - "com.typesafe.akka" %% "akka-http" % "10.1.0-RC1", + akkaActor, + akkaStream, + akkaHttp, // UI - "com.github.fomkin" %% "korolev-server-akkahttp" % "0.7.0", + korolevServerAkkaHttp, // Other - "com.expload" %% "scala-abci-server" % "0.9.2", - "com.github.pureconfig" %% "pureconfig" % "0.9.1", + exploadAbciServer, + pureConfig, // Marshalling - "com.tethys-json" %% "tethys" % "0.7.0.2", - "io.suzaku" %% "boopickle" % "1.2.6", - "com.lightbend.akka" %% "akka-stream-alpakka-unix-domain-socket" % "0.17", - "name.pellet.jp" %% "bsonpickle" % "0.4.4.2", - "com.chuusai" %% "shapeless" % "2.3.3" + tethys, + akkaStreamUnixDomainSocket, + shapeless ), dependencyOverrides += "org.scala-lan" %% "scala-compiler" % "2.12.6", addCompilerPlugin("org.scalamacros" % "paradise" % "2.1.0" cross CrossVersion.full), @@ -259,8 +273,8 @@ lazy val codegen = (project in file("codegen")) .settings(normalizedName := "pravda-codegen") .settings( libraryDependencies ++= Seq( - "org.typelevel" %% "cats-core" % "1.0.1", - "com.github.spullara.mustache.java" % "compiler" % "0.9.5" + catsCore, + javaCompiler )) .settings(scalacOptions ++= Seq( "-Ypartial-unification" @@ -268,6 +282,33 @@ lazy val codegen = (project in file("codegen")) .dependsOn(`vm-asm`) .dependsOn(common % "test->test") +lazy val `node-client` = (project in file("node-client")) + .enablePlugins(RevolverPlugin) + .settings(commonSettings: _*) + .settings(scalacheckOps:_*) + .settings( + name := "pravda-node-client", + normalizedName := "pravda-node-client", + description := "Pravda node client", + ) + .dependsOn(common) + .dependsOn(vm) + .dependsOn(node) + .dependsOn(codegen) + .dependsOn(dotnet) + .dependsOn(evm) + + +// A service for build, sign and broadcast transactions +// within authorized environment +lazy val `broadcaster` = (project in file("services/broadcaster")) + .enablePlugins(UniversalPlugin) + .enablePlugins(ClasspathJarPlugin) + .settings(commonSettings: _*) + .settings(scalacheckOps:_*) + .settings(normalizedName := "pravda-broadcaster") + .dependsOn(`node-client`) + lazy val cli = (project in file("cli")) .enablePlugins(ClasspathJarPlugin) .enablePlugins(BuildInfoPlugin) @@ -279,18 +320,13 @@ lazy val cli = (project in file("cli")) normalizedName := "pravda", mainClass in Compile := Some("pravda.cli.Pravda"), libraryDependencies ++= Seq( - "com.github.scopt" %% "scopt" % "3.7.0", - "org.typelevel" %% "cats-core" % "1.0.1", + scopt, + catsCore, ), bashScriptExtraDefines += """set -- -- "$@"""" ) .dependsOn(yopt) - .dependsOn(common) - .dependsOn(`vm-asm`) - .dependsOn(vm) - .dependsOn(node) - .dependsOn(dotnet) - .dependsOn(codegen) + .dependsOn(`node-client` % "compile->compile;test->test") lazy val `gen-doc` = (project in file("doc") / "gen") .settings(commonSettings: _*) @@ -308,21 +344,22 @@ lazy val testkit = (project in file("testkit")) .settings( skip in publish := true, normalizedName := "pravda-testkit", - unmanagedResourceDirectories in Test += dotnetTests + unmanagedResourceDirectories in Test += dotnetTests, + testFrameworks := Seq(new TestFramework("pravda.common.PreserveColoursFramework")) ) .dependsOn(common % "test->test") .dependsOn(vm % "compile->compile;test->test") .dependsOn(`vm-api`) .dependsOn(`vm-asm`) - .dependsOn(dotnet) + .dependsOn(dotnet % "compile->compile;test->test") .dependsOn(codegen) lazy val yaml4s = (project in file("yaml4s")) .settings(commonSettings: _*) .settings( libraryDependencies ++= Seq( - "org.yaml" % "snakeyaml" % "1.23", - "org.json4s" %% "json4s-native" % "3.6.1" + snakeYml, + json4sNative ) ) @@ -331,6 +368,24 @@ lazy val plaintest = (project in file("plaintest")) .settings(commonSettings: _*) .settings( libraryDependencies ++= Seq( - "com.lihaoyi" %% "utest" % "0.6.3" + uTest + ) + ) + +lazy val faucet = (project in file("faucet")) + .enablePlugins(ClasspathJarPlugin) + .dependsOn(common) + .dependsOn(`node-client`) + .settings(commonSettings: _*) + .settings( + skip in publish := true, + normalizedName := "pravda-faucet", + libraryDependencies ++= Seq( + // Networking + akkaActor, + akkaStream, + akkaHttp, + // UI + korolevServerAkkaHttp ) ) diff --git a/cli/src/main/scala/pravda/cli/Pravda.scala b/cli/src/main/scala/pravda/cli/Pravda.scala index 01a9795f..2cbdec38 100644 --- a/cli/src/main/scala/pravda/cli/Pravda.scala +++ b/cli/src/main/scala/pravda/cli/Pravda.scala @@ -20,7 +20,7 @@ package pravda.cli import akka.actor.ActorSystem import akka.stream.ActorMaterializer import cats.implicits._ -import pravda.cli.languages.impl._ +import pravda.node.client.impl._ import pravda.cli.programs._ import pravda.yopt.CommandLine.{HelpNeeded, Ok, ParseError} @@ -50,6 +50,7 @@ object Pravda extends App { lazy val broadcast = new Broadcast(io, nodeLanguage, compilers) lazy val nodeProgram = new Node(io, random, nodeLanguage) lazy val codegen = new Codegen(io, codeGenerators) + lazy val execute = new Execute(io, nodeLanguage) // FIXME programs should be composed by another one val eventuallyExitCode = PravdaArgsParser.parse(args.toList, PravdaConfig.Nope) match { @@ -59,6 +60,7 @@ object Pravda extends App { case Ok(config: PravdaConfig.Broadcast) => broadcast(config).map(_ => 0) case Ok(config: PravdaConfig.Node) => nodeProgram(config).map(_ => 0) case Ok(config: PravdaConfig.Codegen) => codegen(config).map(_ => 0) + case Ok(config: PravdaConfig.Execute) => execute(config).map(_ => 0) case Ok(PravdaConfig.Nope) => Future { print(PravdaArgsParser.root.toHelpString) diff --git a/cli/src/main/scala/pravda/cli/PravdaArgsParser.scala b/cli/src/main/scala/pravda/cli/PravdaArgsParser.scala index 787d502d..1f82354d 100644 --- a/cli/src/main/scala/pravda/cli/PravdaArgsParser.scala +++ b/cli/src/main/scala/pravda/cli/PravdaArgsParser.scala @@ -64,23 +64,11 @@ object PravdaArgsParser extends CommandLine[PravdaConfig] { .text("Generate auxiliary code to call program's methods from Unity") .action(_ => PravdaConfig.Codegen(CodegenMode.Dotnet)) .children( - opt[File]('d', "dir") - .text("Output directory for generated sources.") - .action { - case (f, conf: PravdaConfig.Codegen) => conf.copy(outDir = Some(f.getAbsolutePath)) - case (_, otherwise) => otherwise - }, opt[File]('i', "input") .text("Input file with assembly.") .action { case (f, conf: PravdaConfig.Codegen) => conf.copy(input = Some(f.getAbsolutePath)) case (_, otherwise) => otherwise - }, - opt[Unit]("exclude-big-integer") - .text("Exclude custom BigInteger class.") - .action { - case ((), conf: PravdaConfig.Codegen) => conf.copy(excludeBigInteger = true) - case (_, otherwise) => otherwise } ) ), @@ -161,7 +149,15 @@ object PravdaArgsParser extends CommandLine[PravdaConfig] { config.copy(mainClass = Some(s)) case (_, otherwise) => otherwise } - ) + ), + cmd("evm") + .text( + "[THIS COMPILATION MODE IS EXPERIMENTAL]" + + "Compile .bin produced by solc compiler to Pravda VM bytecode. " + + "Input files are .bin contract and .abi. " + + "Output is binary Pravda program. " + + "By default read from stdin and print to stdout") + .action(_ => PravdaConfig.Compile(PravdaConfig.CompileMode.Evm)) ), cmd("broadcast") .text("Broadcast transactions and programs to the Pravda blockchain.") @@ -252,6 +248,25 @@ object PravdaArgsParser extends CommandLine[PravdaConfig] { case (_, otherwise) => otherwise }, ), + cmd("execute") + .text("Executes program without side-effects. No watt-limit is required.") + .action(_ => PravdaConfig.Execute()) + .children( + opt[File]('w', "wallet") + .text("File with user wallet. You can obtain it using 'pravda gen address' command. Format: {\"address\": , \"privateKey\": }") + .action { + case (file, config: PravdaConfig.Execute) => + config.copy(wallet = Some(file.getAbsolutePath)) + case (_, otherwise) => otherwise + }, + opt[String]('e', "endpoint") + .text(s"Node endpoint (${DefaultValues.Broadcast.ENDPOINT} by default).") + .action { + case (endpoint, config: PravdaConfig.Execute) => + config.copy(endpoint = endpoint) + case (_, otherwise) => otherwise + } + ), cmd("node") .text("Control Pravda Network Node.") .action(_ => PravdaConfig.Node(PravdaConfig.Node.Mode.Nope, None)) diff --git a/cli/src/main/scala/pravda/cli/PravdaConfig.scala b/cli/src/main/scala/pravda/cli/PravdaConfig.scala index 412b1c3a..3ba88d3d 100644 --- a/cli/src/main/scala/pravda/cli/PravdaConfig.scala +++ b/cli/src/main/scala/pravda/cli/PravdaConfig.scala @@ -32,6 +32,7 @@ object PravdaConfig { case object Asm extends CompileMode case object Disasm extends CompileMode case object DotNet extends CompileMode + case object Evm extends CompileMode } case object Nope extends PravdaConfig @@ -58,6 +59,11 @@ object PravdaConfig { endpoint: String = DefaultValues.Broadcast.ENDPOINT) extends PravdaConfig + final case class Execute(input: Option[String] = None, + wallet: Option[String] = None, + endpoint: String = DefaultValues.Broadcast.ENDPOINT) + extends PravdaConfig + object Broadcast { sealed trait Mode @@ -111,9 +117,5 @@ object PravdaConfig { case object Dotnet extends CodegenMode } - final case class Codegen(codegenMode: CodegenMode, - input: Option[String] = None, - outDir: Option[String] = None, - excludeBigInteger: Boolean = false) - extends PravdaConfig + final case class Codegen(codegenMode: CodegenMode, input: Option[String] = None) extends PravdaConfig } diff --git a/cli/src/main/scala/pravda/cli/programs/Broadcast.scala b/cli/src/main/scala/pravda/cli/programs/Broadcast.scala index 691bd702..810a41a1 100644 --- a/cli/src/main/scala/pravda/cli/programs/Broadcast.scala +++ b/cli/src/main/scala/pravda/cli/programs/Broadcast.scala @@ -22,7 +22,7 @@ import cats.data.EitherT import cats.implicits._ import com.google.protobuf.ByteString import pravda.cli.PravdaConfig -import pravda.cli.languages.{CompilersLanguage, IoLanguage, NodeLanguage} +import pravda.node.client.{CompilersLanguage, IoLanguage, NodeLanguage} import pravda.common.bytes import pravda.common.contrib.ed25519 import pravda.common.domain.Address diff --git a/cli/src/main/scala/pravda/cli/programs/Codegen.scala b/cli/src/main/scala/pravda/cli/programs/Codegen.scala index 07b46e8b..4b78b085 100644 --- a/cli/src/main/scala/pravda/cli/programs/Codegen.scala +++ b/cli/src/main/scala/pravda/cli/programs/Codegen.scala @@ -22,7 +22,7 @@ import cats.data.EitherT import cats.implicits._ import pravda.cli.PravdaConfig import pravda.cli.PravdaConfig.CodegenMode.Dotnet -import pravda.cli.languages.{CodeGeneratorsLanguage, IoLanguage} +import pravda.node.client.{CodeGeneratorsLanguage, IoLanguage} import scala.language.higherKinds @@ -37,15 +37,13 @@ class Codegen[F[_]: Monad](io: IoLanguage[F], codegen: CodeGeneratorsLanguage[F] ) result <- EitherT[F, String, List[(String, String)]] { config.codegenMode match { - case Dotnet => codegen.dotnet(input, config.excludeBigInteger).map(Right(_)) + case Dotnet => codegen.dotnet(input).map(Right(_)) } } } yield { result } - val dir = config.outDir.getOrElse("codegen") - errorOrResult.value.flatMap { case Left(error) => io.writeStringToStderrAndExit(s"$error\n") case Right(result) => @@ -53,8 +51,8 @@ class Codegen[F[_]: Monad](io: IoLanguage[F], codegen: CodeGeneratorsLanguage[F] .map { case (filename, content) => for { - _ <- io.mkdirs(dir) - path <- io.concatPath(dir, filename) + _ <- io.mkdirs("Assets") + path <- io.concatPath("Assets", filename) _ <- io.writeStringToStdout(s"Writing to $path\n") _ <- io.writeToFile(path, content) } yield () diff --git a/cli/src/main/scala/pravda/cli/programs/Compile.scala b/cli/src/main/scala/pravda/cli/programs/Compile.scala index 519fb86e..a2af54db 100644 --- a/cli/src/main/scala/pravda/cli/programs/Compile.scala +++ b/cli/src/main/scala/pravda/cli/programs/Compile.scala @@ -23,7 +23,7 @@ import cats.implicits._ import com.google.protobuf.ByteString import pravda.cli.PravdaConfig import pravda.cli.PravdaConfig.CompileMode -import pravda.cli.languages.{CompilersLanguage, IoLanguage} +import pravda.node.client.{CompilersLanguage, IoLanguage} import scala.language.higherKinds @@ -70,8 +70,8 @@ class Compile[F[_]: Monad](io: IoLanguage[F], compilers: CompilersLanguage[F]) { } case DotNet => - val (other, csfiles) = inputs.partition { - case (p, f) => !p.endsWith(".exe") && !p.endsWith(".dll") && !p.endsWith(".pdb") && p != "stdin" + val csfiles = inputs.filter { + case (p, f) => p.endsWith(".exe") || p.endsWith(".dll") || p.endsWith(".pdb") || p == "stdin" } val dotnetFilesE: Either[String, List[(ByteString, Option[ByteString])]] = @@ -97,20 +97,28 @@ class Compile[F[_]: Monad](io: IoLanguage[F], compilers: CompilersLanguage[F]) { .toList .sequence - val compiled = dotnetFilesE match { + val compiled: F[Either[String, ByteString]] = dotnetFilesE match { case Left(err) => Monad[F].pure(Left(err)) case Right(dotnetFiles) => compilers.dotnet(dotnetFiles, config.mainClass) } - for { - _ <- if (other.nonEmpty) { - io.writeStringToStdout( - other.map(o => s"Warning: ${o._1} has wrong file extension").mkString("", "\n", "\n")) - } else { - Monad[F].pure(()) - } - c <- compiled - } yield c + compiled + + case Evm => + val err: F[Either[String, ByteString]] = Monad[F].pure(Left( + "Required 2 file .abi and .bin with identical names(Use https://solidity.readthedocs.io/en/latest/installing-solidity.html)")) + if (inputs.size == 2) { + val compiled = for { + bin <- inputs.collectFirst { case r @ (f, _) if f.endsWith(".bin") => r } + abi <- inputs.collectFirst { case r @ (f, _) if f.endsWith(".abi") => r } + binName = bin._1.stripSuffix(".bin") + abiName = abi._1.stripSuffix(".abi") + if binName == abiName + } yield compilers.evm(bin._2, abi._2) + + compiled.getOrElse(err) + } else err + case Nope => Monad[F].pure(Left("Compilation mode should be selected.")) } } diff --git a/cli/src/main/scala/pravda/cli/programs/Execute.scala b/cli/src/main/scala/pravda/cli/programs/Execute.scala new file mode 100644 index 00000000..6bd18006 --- /dev/null +++ b/cli/src/main/scala/pravda/cli/programs/Execute.scala @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2018 Expload.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package pravda.cli.programs + +import cats._ +import cats.data.EitherT +import cats.implicits._ +import com.google.protobuf.ByteString +import pravda.cli.PravdaConfig +import pravda.cli.programs.Broadcast.Wallet +import pravda.node.client.{IoLanguage, NodeLanguage} +import pravda.node.data.serialization._ +import pravda.node.data.serialization.json._ + +import scala.language.higherKinds + +class Execute[F[_]: Monad](io: IoLanguage[F], api: NodeLanguage[F]) { + + val readFromFile = (path: String) => + io.readFromFile(path) + .map(_.toRight(s"`$path` is not found.")) + + def apply(config: PravdaConfig.Execute): F[Unit] = { + val errorOrResult: EitherT[F, String, String] = + for { + walletJson <- EitherT( + config.wallet + .fold[F[Either[String, ByteString]]](Monad[F].pure(Left("Wallet file should be defined")))(readFromFile)) + program <- useOption(config.input)(io.readFromStdin(), readFromFile) + wallet = transcode(Json @@ walletJson.toStringUtf8).to[Wallet] + result <- EitherT { + api.execute(program, wallet.address, config.endpoint) + } + } yield transcode(result).to[Json] + errorOrResult.value.flatMap { + case Left(error) => io.writeStringToStderrAndExit(s"$error\n") + case Right(result) => io.writeStringToStdout(s"$result\n") + } + } +} diff --git a/cli/src/main/scala/pravda/cli/programs/GenAddress.scala b/cli/src/main/scala/pravda/cli/programs/GenAddress.scala index cae5bd07..318c0a7b 100644 --- a/cli/src/main/scala/pravda/cli/programs/GenAddress.scala +++ b/cli/src/main/scala/pravda/cli/programs/GenAddress.scala @@ -21,7 +21,7 @@ import cats._ import cats.implicits._ import com.google.protobuf.ByteString import pravda.cli.PravdaConfig -import pravda.cli.languages.{IoLanguage, RandomLanguage} +import pravda.node.client.{IoLanguage, RandomLanguage} import pravda.common.{bytes, crypto} import scala.language.higherKinds diff --git a/cli/src/main/scala/pravda/cli/programs/Node.scala b/cli/src/main/scala/pravda/cli/programs/Node.scala index 2f2e0646..37d7d813 100644 --- a/cli/src/main/scala/pravda/cli/programs/Node.scala +++ b/cli/src/main/scala/pravda/cli/programs/Node.scala @@ -23,7 +23,7 @@ import cats.implicits._ import com.google.protobuf.ByteString import pravda.cli.PravdaConfig import pravda.cli.PravdaConfig.Node.{Mode, Network} -import pravda.cli.languages.{IoLanguage, NodeLanguage, RandomLanguage} +import pravda.node.client.{IoLanguage, NodeLanguage, RandomLanguage} import pravda.common.domain.{Address, NativeCoin} import pravda.common.{bytes, crypto} import pravda.node.data.PravdaConfig.Validator @@ -65,7 +65,6 @@ final class Node[F[_]: Monad](io: IoLanguage[F], random: RandomLanguage[F], node | chain-id = "$chainId" | validators = "${validators.mkString(",")}" | app-hash = "" - | distribution = true | } |${if (isValidator) { s""" validator { diff --git a/cli/src/main/scala/pravda/cli/programs/RunBytecode.scala b/cli/src/main/scala/pravda/cli/programs/RunBytecode.scala index 467eb24e..75364947 100644 --- a/cli/src/main/scala/pravda/cli/programs/RunBytecode.scala +++ b/cli/src/main/scala/pravda/cli/programs/RunBytecode.scala @@ -21,7 +21,7 @@ import cats._ import cats.data.EitherT import cats.implicits._ import pravda.cli.PravdaConfig -import pravda.cli.languages.{IoLanguage, VmLanguage} +import pravda.node.client.{IoLanguage, VmLanguage} import pravda.common.bytes import pravda.node.data.serialization._ import pravda.node.data.serialization.json._ diff --git a/cli/src/test/scala/pravda/cli/programs/BroadcastSuite.scala b/cli/src/test/scala/pravda/cli/programs/BroadcastSuite.scala index 1bec5041..8fba4ad8 100644 --- a/cli/src/test/scala/pravda/cli/programs/BroadcastSuite.scala +++ b/cli/src/test/scala/pravda/cli/programs/BroadcastSuite.scala @@ -3,7 +3,8 @@ package pravda.cli.programs import cats.Id import com.google.protobuf.ByteString import pravda.cli.PravdaConfig -import pravda.cli.languages.{CompilersLanguage, IoLanguageStub, NodeLanguageStub} +import pravda.node.client.{CompilersLanguage, IoLanguageStub, NodeLanguageStub} +import pravda.node.data.common.TransactionId import pravda.node.servers.Abci.TransactionResult import pravda.vm.FinalState import pravda.vm.asm.Operation @@ -15,6 +16,7 @@ object BroadcastSuite extends TestSuite { final val expectedResult = s"""{ + | "transactionId" : "", | "executionResult" : { | "success" : { | "spentWatts" : 0, @@ -37,7 +39,8 @@ object BroadcastSuite extends TestSuite { val tests = Tests { "run -w w.json" - { - val api = new NodeLanguageStub(Right(TransactionResult(Right(FinalState.Empty), Nil))) + val api = + new NodeLanguageStub(Right(TransactionResult(TransactionId @@ ByteString.EMPTY, Right(FinalState.Empty), Nil))) val io = new IoLanguageStub(files = mutable.Map("w.json" -> Wallet)) val compilers = new CompilersLanguage[Id] { def asm(fileName: String, source: String): Id[Either[String, ByteString]] = Left("nope") @@ -46,6 +49,7 @@ object BroadcastSuite extends TestSuite { def disasmToOps(source: ByteString): Id[Seq[(Int, Operation)]] = Nil def dotnet(sources: Seq[(ByteString, Option[ByteString])], mainClass: Option[String]): Id[Either[String, ByteString]] = Left("nope") + def evm(source: ByteString, abi: ByteString): Id[Either[String, ByteString]] = Left("nope") } val program = new Broadcast(io, api, compilers) program(PravdaConfig.Broadcast(mode = PravdaConfig.Broadcast.Mode.Run, wallet = Some("w.json"))) @@ -53,7 +57,8 @@ object BroadcastSuite extends TestSuite { } "run" - { - val api = new NodeLanguageStub(Right(TransactionResult(Right(FinalState.Empty), Nil))) + val api = + new NodeLanguageStub(Right(TransactionResult(TransactionId @@ ByteString.EMPTY, Right(FinalState.Empty), Nil))) val io = new IoLanguageStub() val compilers = new CompilersLanguage[Id] { def asm(source: String): Id[Either[String, ByteString]] = Left("nope") @@ -62,6 +67,8 @@ object BroadcastSuite extends TestSuite { def disasm(source: ByteString): Id[String] = "" def dotnet(sources: Seq[(ByteString, Option[ByteString])], mainClass: Option[String]): Id[Either[String, ByteString]] = Left("nope") + + def evm(source: ByteString, abi: ByteString): Id[Either[String, ByteString]] = Left("nope") } val program = new Broadcast(io, api, compilers) program(PravdaConfig.Broadcast(mode = PravdaConfig.Broadcast.Mode.Run)) diff --git a/cli/src/test/scala/pravda/cli/programs/CompileSuite.scala b/cli/src/test/scala/pravda/cli/programs/CompileSuite.scala index bba0148d..4ff4a879 100644 --- a/cli/src/test/scala/pravda/cli/programs/CompileSuite.scala +++ b/cli/src/test/scala/pravda/cli/programs/CompileSuite.scala @@ -4,7 +4,7 @@ import cats.Id import com.google.protobuf.ByteString import pravda.cli.PravdaConfig import pravda.cli.PravdaConfig.CompileMode -import pravda.cli.languages.{CompilersLanguage, IoLanguageStub} +import pravda.node.client.{CompilersLanguage, IoLanguageStub} import pravda.vm.asm.Operation import utest._ @@ -34,6 +34,8 @@ object CompileSuite extends TestSuite { def dotnet(sources: Seq[(ByteString, Option[ByteString])], mainClass: Option[String]): Id[Either[String, ByteString]] = Right(UnexpectedBinaryOutput) def disasmToOps(source: ByteString): Id[Seq[(Int, Operation)]] = Nil + + override def evm(source: ByteString, abi: ByteString): Id[Either[String, ByteString]] = ??? } val compile = new Compile[Id](io, compilers) compile(PravdaConfig.Compile(Asm)) @@ -52,6 +54,8 @@ object CompileSuite extends TestSuite { def dotnet(sources: Seq[(ByteString, Option[ByteString])], mainClass: Option[String]): Id[Either[String, ByteString]] = Right(UnexpectedBinaryOutput) def disasmToOps(source: ByteString): Id[Seq[(Int, Operation)]] = Nil + + def evm(source: ByteString, abi: ByteString): Id[Either[String, ByteString]] = Right(UnexpectedBinaryOutput) } val compile = new Compile[Id](io, compilers) compile(PravdaConfig.Compile(Disasm)) @@ -73,10 +77,12 @@ object CompileSuite extends TestSuite { else Right(UnexpectedBinaryOutput) } def disasmToOps(source: ByteString): Id[Seq[(Int, Operation)]] = Nil + def evm(source: ByteString, abi: ByteString): Id[Either[String, ByteString]] = Right(UnexpectedBinaryOutput) } val compile = new Compile[Id](io, compilers) compile(PravdaConfig.Compile(DotNet)) assert(io.stdout.headOption.contains(ExpectedBinaryOutput)) } + } } diff --git a/cli/src/test/scala/pravda/cli/programs/GenAddressSuite.scala b/cli/src/test/scala/pravda/cli/programs/GenAddressSuite.scala index fd56e8e9..944951dd 100644 --- a/cli/src/test/scala/pravda/cli/programs/GenAddressSuite.scala +++ b/cli/src/test/scala/pravda/cli/programs/GenAddressSuite.scala @@ -2,7 +2,7 @@ package pravda.cli.programs import com.google.protobuf.ByteString import pravda.cli.PravdaConfig -import pravda.cli.languages._ +import pravda.node.client._ import utest._ object GenAddressSuite extends TestSuite { diff --git a/cli/src/test/scala/pravda/cli/programs/RunBytecodeSuite.scala b/cli/src/test/scala/pravda/cli/programs/RunBytecodeSuite.scala index ce442123..9dcf8761 100644 --- a/cli/src/test/scala/pravda/cli/programs/RunBytecodeSuite.scala +++ b/cli/src/test/scala/pravda/cli/programs/RunBytecodeSuite.scala @@ -3,7 +3,7 @@ package pravda.cli.programs import cats.Id import com.google.protobuf.ByteString import pravda.cli.PravdaConfig -import pravda.cli.languages._ +import pravda.node.client._ import pravda.vm.impl.MemoryImpl import pravda.vm.{Data, ExecutionResult, FinalState} import utest._ diff --git a/codegen/src/main/resources/DotnetHttpMethods.mustache.cs b/codegen/src/main/resources/DotnetHttpMethods.mustache.cs deleted file mode 100644 index 26b75af2..00000000 --- a/codegen/src/main/resources/DotnetHttpMethods.mustache.cs +++ /dev/null @@ -1,81 +0,0 @@ -using System; -using System.Collections; -using UnityEngine; -using UnityEngine.Networking; - -namespace Expload.Pravda.{{programName}} { - {{#parseClasses}} - [System.Serializable] - class {{resultTpeClass}}Result { - public {{resultTpe}} value; - public string tpe; - - public static {{resultTpeClass}}Result FromJson(string json) { - return JsonUtility.FromJson<{{resultTpeClass}}Result>(json); - } - } - {{/parseClasses}} - - abstract class ProgramRequest - { - public byte[] ProgramAddress { get; protected set; } - - public T Result { get; protected set; } - public string Error { get; protected set; } - public bool IsError { get; protected set; } - - protected ProgramRequest(byte[] programAddress) - { - ProgramAddress = programAddress; - IsError = false; - Error = ""; - } - - protected abstract T ParseResult(string json); - - protected IEnumerator SendJson(string json) - { - UnityWebRequest www = UnityWebRequest.Put("{{client}}", json); - www.method = "POST"; - www.SetRequestHeader("Content-Type", "application/json"); - - yield return www.SendWebRequest(); - - if (www.isNetworkError || www.isHttpError) - { - IsError = true; - Error = www.error; - } - else - { - try - { - Result = ParseResult(www.downloadHandler.text); - } - catch (ArgumentException e) - { - IsError = true; - Error = "Invalid JSON: " + www.downloadHandler.text + "\n" + e.Message; - } - } - } - } - - {{#methods}} - class {{methodName}}Request: ProgramRequest<{{methodTpe}}> { - - public {{methodName}}Request(byte[] programAddress) : base(programAddress) { } - - protected override {{methodTpe}} ParseResult(string json) - { - return {{methodParseResult}}; - } - - public IEnumerator {{methodName}}({{methodArgsDef}}) - { - String json = String.Format("{{{jsonFormat}}}", {{{argsFormat}}}); - yield return SendJson(json); - } - } - {{/methods}} -} \ No newline at end of file diff --git a/codegen/src/main/resources/ExploadPravdaProgram.cs.mustache b/codegen/src/main/resources/ExploadPravdaProgram.cs.mustache new file mode 100644 index 00000000..7e5c9ac6 --- /dev/null +++ b/codegen/src/main/resources/ExploadPravdaProgram.cs.mustache @@ -0,0 +1,35 @@ +using System; +using System.Collections; +using Expload.Unity.Codegen; + +namespace Expload.Pravda.{{programName}} +{ + {{#methods}} + public class {{methodName}}Request: ProgramRequest<{{methodTpe}}> + { + public {{methodName}}Request(byte[] programAddress) : base(programAddress) { } + + protected override {{methodTpe}} ParseResult(string elem) + { + return ExploadTypeConverters.{{methodParseResult}}(elem); + } + + public IEnumerator Test({{methodArgs}}) + { + yield return SendRequest("{{methodName}}", new string[] { {{methodPrintArgs}} }, true); + } + + public IEnumerator Call({{methodArgs}}) + { + yield return SendRequest("{{methodName}}", new string[] { {{methodPrintArgs}} }, false); + } + + // Same as Call + // Deprecated + public IEnumerator {{methodName}}({{methodArgs}}) + { + yield return SendRequest("{{methodName}}", new string[] { {{methodPrintArgs}} }, false); + } + } + {{/methods}} +} \ No newline at end of file diff --git a/codegen/src/main/resources/ExploadUnityCodegen.cs b/codegen/src/main/resources/ExploadUnityCodegen.cs new file mode 100644 index 00000000..db3e3e75 --- /dev/null +++ b/codegen/src/main/resources/ExploadUnityCodegen.cs @@ -0,0 +1,246 @@ +using System; +using System.Collections; +using UnityEngine; +using UnityEngine.Networking; +using Newtonsoft.Json; + +namespace Expload.Unity.Codegen +{ + public static class ExploadTypeConverters + { + // signed integers + + public static sbyte ParseInt8(string elem) + { + if (elem.StartsWith("int8.")) { + return sbyte.Parse(elem.Substring("int8.".Length)); + } else { + throw new ArgumentException("Wrong format for int8 type: " + elem); + } + } + + public static string PrintInt8(sbyte elem) + { + return "int8." + elem.ToString(); + } + + public static short ParseInt16(string elem) + { + if (elem.StartsWith("int16.")) { + return short.Parse(elem.Substring("int16.".Length)); + } else { + throw new ArgumentException("Wrong format for int16 type: " + elem); + } + } + + public static string PrintInt16(short elem) + { + return "int16." + elem.ToString(); + } + + public static int ParseInt32(string elem) + { + if (elem.StartsWith("int32.")) { + return int.Parse(elem.Substring("int32.".Length)); + } else { + throw new ArgumentException("Wrong format for int32 type: " + elem); + } + } + + public static string PrintInt32(int elem) + { + return "int32." + elem.ToString(); + } + + public static long ParseInt64(string elem) + { + if (elem.StartsWith("int64.")) { + return long.Parse(elem.Substring("int64.".Length)); + } else { + throw new ArgumentException("Wrong format for int64 type: " + elem); + } + } + + public static string PrintInt64(long elem) + { + return "int64." + elem.ToString(); + } + + // bool + + public static bool ParseBool(string elem) + { + if (elem == "bool.true") { + return true; + } else if (elem == "bool.false") { + return false; + } else { + throw new ArgumentException("Wrong format for bool type: " + elem); + } + } + + public static string PrintBool(bool elem) + { + return "bool." + (elem ? "true" : "false"); + } + + // utf8 + + public static string ParseUtf8(string elem) + { + if (elem.StartsWith("utf8.")) { + return elem.Substring("utf8.".Length); + } else { + throw new ArgumentException("Wrong format for utf8 type: " + elem); + } + } + + public static string PrintUtf8(string elem) + { + return "utf8." + elem; + } + + // bytes + + public static byte[] ParseBytes(string elem) + { + if (elem.StartsWith("bytes.")) { + string hex = elem.Substring("bytes.".Length); + byte[] bytes = new byte[hex.Length / 2]; + for (int i = 0; i < hex.Length; i += 2) { + bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16); + } + return bytes; + } else { + throw new ArgumentException("Wrong format for int32 type: " + elem); + } + } + + public static string PrintBytes(byte[] elem) + { + return "bytes." + BitConverter.ToString(elem).Replace("-", ""); + } + + // null + + public static object ParseNull(string elem) + { + if (elem == "null") { + return null; + } else { + throw new ArgumentException("Wrong format for null type: " + elem); + } + } + + public static string PrintNull(object elem) + { + return "null"; + } + } + + public class ExploadMethodRequest + { + public string address { get; set; } + public string method { get; set; } + public string[] args { get; set; } + + public ExploadMethodRequest(string address, string method, string[] args) + { + this.address = address; + this.method = method; + this.args = args; + } + } + + public class ExploadResponseFinalState + { + public long spentWatts { get; set; } + public long refundWatts { get; set; } + public long totalWatts { get; set; } + public string[] stack { get; set; } + } + + public class ExploadResponseData + { + public ExploadResponseFinalState finalState { get; set; } + } + + public class ExploadResponse + { + public string transactionId { get; set; } + public string error { get; set; } + public string errorCode { get; set; } + public ExploadResponseData data { get; set; } + } + + public abstract class ProgramRequest + { + public byte[] ProgramAddress { get; protected set; } + + public T Result { get; protected set; } + public string TransactionId { get; protected set; } + public string Error { get; protected set; } + public bool IsError { get; protected set; } + + protected ProgramRequest(byte[] programAddress) + { + ProgramAddress = programAddress; + IsError = false; + Error = ""; + } + + protected abstract T ParseResult(string elem); + + protected IEnumerator SendRequest(string method, string[] args, bool test) + { + var request = new ExploadMethodRequest(BitConverter.ToString(ProgramAddress).Replace("-", ""), method, args); + string json = JsonConvert.SerializeObject(request); + + string uri = ""; + if (test) { + uri = "localhost:8087/api/program/method-test"; + } else { + uri = "localhost:8087/api/program/method"; + } + + UnityWebRequest www = UnityWebRequest.Put(uri, json); + www.method = "POST"; + www.SetRequestHeader("Content-Type", "application/json"); + + yield return www.SendWebRequest(); + + if (www.isNetworkError || www.isHttpError) + { + IsError = true; + Error = www.error + "\nHttp code: " + www.responseCode + "\nText: " + www.downloadHandler.text; + } + else + { + try + { + var response = JsonConvert.DeserializeObject(www.downloadHandler.text); + TransactionId = response.transactionId; + if (response.error.Length != 0) { + IsError = true; + Error = "Error from response: " + response.error; + } else if (response.data.finalState.stack.Length > 1) { + IsError = true; + Error = "Invalid method result:\n[" + String.Join(", ", response.data.finalState.stack) + "]"; + } else if (response.data.finalState.stack.Length == 1) { + Result = ParseResult(response.data.finalState.stack[0]); + } + } + catch (JsonSerializationException e) + { + IsError = true; + Error = "Invalid JSON:\n" + www.downloadHandler.text + "\nError: " + e.Message; + } + catch (ArgumentException e) + { + IsError = true; + Error = "Invalid JSON:\n" + www.downloadHandler.text + "\nError: " + e.Message; + } + } + } + } +} \ No newline at end of file diff --git a/codegen/src/main/scala/pravda/codegen/dotnet/DotnetCodegen.scala b/codegen/src/main/scala/pravda/codegen/dotnet/DotnetCodegen.scala index a1651679..9ba0393f 100644 --- a/codegen/src/main/scala/pravda/codegen/dotnet/DotnetCodegen.scala +++ b/codegen/src/main/scala/pravda/codegen/dotnet/DotnetCodegen.scala @@ -26,6 +26,7 @@ import pravda.vm.Meta.ProgramName import pravda.vm.asm import scala.collection.JavaConverters._ +import scala.io.Source object DotnetCodegen { @@ -34,59 +35,52 @@ object DotnetCodegen { final case class ParseClass(resultTpeClass: String, resultTpe: String) final case class MethodTemplate(methodName: String, methodTpe: String, - methodArgsDef: String, + methodParseResult: String, methodArgs: String, - jsonFormat: String, - argsFormat: String, - methodParseResult: String) - final case class DotnetTemplate(programName: String, - methods: java.util.List[MethodTemplate], - parseClasses: java.util.List[ParseClass], - client: String) - - private def tpeToDotnetFormat(tpe: Meta.TypeSignature): String => String = { - def primitiveFormat(p: Meta.TypeSignature.PrimitiveType): String => String = p match { - case Meta.TypeSignature.Null => ??? - case Meta.TypeSignature.Int8 => identity - case Meta.TypeSignature.Int16 => identity - case Meta.TypeSignature.Int32 => identity - case Meta.TypeSignature.BigInt => ??? - case Meta.TypeSignature.Uint8 => identity - case Meta.TypeSignature.Uint16 => identity - case Meta.TypeSignature.Uint32 => identity - case Meta.TypeSignature.Number => identity - case Meta.TypeSignature.Boolean => - arg => - s"""$arg ? "true" : "false" """ - case Meta.TypeSignature.Ref => ??? - case Meta.TypeSignature.Utf8 => - arg => - s""" "\\"" + $arg + "\\"" """ - case Meta.TypeSignature.Bytes => - arg => - s""" "\\"" + BitConverter.ToString($arg).Replace("-","") + "\\"" """ + methodPrintArgs: String) + final case class DotnetTemplate(programName: String, methods: java.util.List[MethodTemplate]) + + private def dotnetPrintArg(tpe: Meta.TypeSignature): String = { + + def primitivePrint(p: Meta.TypeSignature.PrimitiveType): String = p match { + case Meta.TypeSignature.Null => "PrintNull" + case Meta.TypeSignature.Int8 => "PrintInt8" + case Meta.TypeSignature.Int16 => "PrintInt16" + case Meta.TypeSignature.Int32 => "PrintInt32" + case Meta.TypeSignature.Int64 => "PrintInt64" + case Meta.TypeSignature.BigInt => ??? //"PrintBigInt" + case Meta.TypeSignature.Number => ??? //"PrintNumber" + case Meta.TypeSignature.Boolean => "PrintBool" + case Meta.TypeSignature.Ref => ??? + case Meta.TypeSignature.Utf8 => "PrintUtf8" + case Meta.TypeSignature.Bytes => "PrintBytes" } tpe match { - case p: Meta.TypeSignature.PrimitiveType => primitiveFormat(p) - case Meta.TypeSignature.Array(p) => - arg => - s""" "[" + string.Join(",", $arg) + "]" """ // FIXME doesn't work for bool, utf8, bytes - case Meta.TypeSignature.Struct(name, fields) => ??? + case p: Meta.TypeSignature.PrimitiveType => primitivePrint(p) + case Meta.TypeSignature.Array(p) => s"${primitivePrint(p)}Array" + case Meta.TypeSignature.Struct(sname, fields) => ??? } } - private def tpeToDotnetTpe(tpe: Meta.TypeSignature): String = { + private def dotnetPringArgs(m: Meta.MethodSignature): String = + m.args + .zip(argsNames(m)) + .map { + case (tpe, name) => s"ExploadTypeConverters.${dotnetPrintArg(tpe)}($name)" + } + .mkString(", ") + + private def dotnetTpe(tpe: Meta.TypeSignature): String = { + def primitiveToDotnetTpe(p: Meta.TypeSignature.PrimitiveType): String = p match { case Meta.TypeSignature.Null => "object" case Meta.TypeSignature.Int8 => "sbyte" case Meta.TypeSignature.Int16 => "short" case Meta.TypeSignature.Int32 => "int" + case Meta.TypeSignature.Int64 => "long" case Meta.TypeSignature.BigInt => "BigInteger" - case Meta.TypeSignature.Uint8 => "byte" - case Meta.TypeSignature.Uint16 => "ushort" - case Meta.TypeSignature.Uint32 => "uint" case Meta.TypeSignature.Number => ??? case Meta.TypeSignature.Boolean => "bool" case Meta.TypeSignature.Ref => ??? @@ -101,57 +95,49 @@ object DotnetCodegen { } } - private def argsNames(method: Meta.MethodSignature): List[String] = - method.args.indices.map(i => s"arg$i").toList + private def dotnetParseResult(tpe: Meta.TypeSignature): String = { - private def jsonArgsFormat(method: Meta.MethodSignature): (String, String) = { - val argsFormat = method.args.zipWithIndex - .map { - case (tpe, i) => - s"""{{ \\"value\\": {${i + 1}}, \\"tpe\\": \\"${tpe.mnemonic}\\" }}""" + def primitiveToDotnetTpe(p: Meta.TypeSignature.PrimitiveType): String = + p match { + case Meta.TypeSignature.Null => "ParseNull" + case Meta.TypeSignature.Int8 => "ParseInt8" + case Meta.TypeSignature.Int16 => "ParseInt16" + case Meta.TypeSignature.Int32 => "ParseInt32" + case Meta.TypeSignature.Int64 => "ParseInt64" + case Meta.TypeSignature.BigInt => "ParseBigInt" + case Meta.TypeSignature.Number => "ParseNumber" + case Meta.TypeSignature.Boolean => "ParseBool" + case Meta.TypeSignature.Ref => ??? + case Meta.TypeSignature.Utf8 => "ParseUtf8" + case Meta.TypeSignature.Bytes => "ParseBytes" } - .mkString(", ") - val args = "ProgramAddress" +: argsNames(method) - val tpes = Meta.TypeSignature.Bytes +: method.args - - ( - s"""{{ \\"address\\": {0}, \\"method\\": \\"${method.name}\\", \\"args\\": [$argsFormat] }}""", - tpes.map(tpeToDotnetFormat).zip(args).map { case (formatF, arg) => formatF(arg) }.mkString(", ") - ) + tpe match { + case p: Meta.TypeSignature.PrimitiveType => primitiveToDotnetTpe(p) + case Meta.TypeSignature.Array(p) => s"${primitiveToDotnetTpe(p)}Array" + case Meta.TypeSignature.Struct(sname, fields) => ??? + } } - private def consturctTemplate(programName: String, methods: List[Meta.MethodSignature]): DotnetTemplate = { + private def argsNames(method: Meta.MethodSignature): List[String] = + method.args.indices.map(i => s"arg$i").toList - def constructArgs(method: Meta.MethodSignature): (String, String) = { - (method.args.zip(argsNames(method)).map { case (tpe, name) => s"${tpeToDotnetTpe(tpe)} $name" }.mkString(", "), - argsNames(method).mkString(", ")) - } + private def dotnetArgs(method: Meta.MethodSignature) = + method.args.zip(argsNames(method)).map { case (tpe, name) => s"${dotnetTpe(tpe)} $name" }.mkString(", ") + private def consturctTemplate(programName: String, methods: List[Meta.MethodSignature]) = DotnetTemplate( - programName.capitalize, + programName, methods - .map(m => { - val (jsonFormat, argsFormat) = jsonArgsFormat(m) - val (argsDef, args) = constructArgs(m) - val tpe = tpeToDotnetTpe(m.returnTpe) - val parseResult = if (m.returnTpe == Meta.TypeSignature.Null) { - "null" - } else { - s"${tpe.capitalize}Result.FromJson(json).value" - } - MethodTemplate(m.name.capitalize, tpe, argsDef, args, jsonFormat, argsFormat, parseResult) - }) - .asJava, - methods - .filter(_.returnTpe != Meta.TypeSignature.Null) - .map(m => tpeToDotnetTpe(m.returnTpe)) - .distinct - .map(t => ParseClass(t.capitalize, t)) - .asJava, - "localhost:8087/api/program/method" + .map( + m => + MethodTemplate(m.name, + dotnetTpe(m.returnTpe), + dotnetParseResult(m.returnTpe), + dotnetArgs(m), + dotnetPringArgs(m))) + .asJava ) - } def extractInfo(bytecode: ByteString): (String, List[Meta.MethodSignature]) = { val ops = asm.PravdaAssembler.disassemble(bytecode) @@ -169,14 +155,17 @@ object DotnetCodegen { def generateMethods(programName: String, methods: List[Meta.MethodSignature]): String = { val mf = new DefaultMustacheFactory - val mustache = mf.compile("DotnetHttpMethods.mustache.cs") + val mustache = mf.compile("ExploadPravdaProgram.cs.mustache") val sw = new StringWriter() mustache.execute(sw, consturctTemplate(programName, methods)) sw.toString } - def generate(byteCode: ByteString): GeneratedFile = { // (BigInteger, Methods) + def generate(byteCode: ByteString): Seq[GeneratedFile] = { val (name, methods) = extractInfo(byteCode) - (name.capitalize + ".cs", generateMethods(name, methods.filter(_.name != "ctor"))) + Seq( + (name.capitalize + ".cs", generateMethods(name, methods.filter(_.name != "ctor"))), + ("ExploadUnityCodegen.cs", Source.fromResource("ExploadUnityCodegen.cs").mkString) + ) } } diff --git a/codegen/src/test/resources/ERC20.generated.cs b/codegen/src/test/resources/ERC20.generated.cs index d2f98b34..b09de983 100644 --- a/codegen/src/test/resources/ERC20.generated.cs +++ b/codegen/src/test/resources/ERC20.generated.cs @@ -1,137 +1,137 @@ using System; using System.Collections; -using UnityEngine; -using UnityEngine.Networking; - -namespace Expload.Pravda.ERC20 { - [System.Serializable] - class UintResult { - public uint value; - public string tpe; - - public static UintResult FromJson(string json) { - return JsonUtility.FromJson(json); - } - } +using Expload.Unity.Codegen; - abstract class ProgramRequest +namespace Expload.Pravda.ERC20 +{ + public class BalanceOfRequest: ProgramRequest { - public byte[] ProgramAddress { get; protected set; } - - public T Result { get; protected set; } - public string Error { get; protected set; } - public bool IsError { get; protected set; } + public BalanceOfRequest(byte[] programAddress) : base(programAddress) { } - protected ProgramRequest(byte[] programAddress) + protected override long ParseResult(string elem) { - ProgramAddress = programAddress; - IsError = false; - Error = ""; + return ExploadTypeConverters.ParseInt64(elem); } - protected abstract T ParseResult(string json); - - protected IEnumerator SendJson(string json) + public IEnumerator Test(byte[] arg0) { - UnityWebRequest www = UnityWebRequest.Put("localhost:8087/api/program/method", json); - www.method = "POST"; - www.SetRequestHeader("Content-Type", "application/json"); - - yield return www.SendWebRequest(); - - if (www.isNetworkError || www.isHttpError) - { - IsError = true; - Error = www.error; - } - else - { - try - { - Result = ParseResult(www.downloadHandler.text); - } - catch (ArgumentException e) - { - IsError = true; - Error = "Invalid JSON: " + www.downloadHandler.text + "\n" + e.Message; - } - } + yield return SendRequest("BalanceOf", new string[] { ExploadTypeConverters.PrintBytes(arg0) }, true); } - } - - class BalanceOfRequest: ProgramRequest { - public BalanceOfRequest(byte[] programAddress) : base(programAddress) { } - - protected override uint ParseResult(string json) + public IEnumerator Call(byte[] arg0) { - return UintResult.FromJson(json).value; + yield return SendRequest("BalanceOf", new string[] { ExploadTypeConverters.PrintBytes(arg0) }, false); } + // Same as Call + // Deprecated public IEnumerator BalanceOf(byte[] arg0) { - String json = String.Format("{{ \"address\": {0}, \"method\": \"balanceOf\", \"args\": [{{ \"value\": {1}, \"tpe\": \"bytes\" }}] }}", "\"" + BitConverter.ToString(ProgramAddress).Replace("-","") + "\"" , "\"" + BitConverter.ToString(arg0).Replace("-","") + "\"" ); - yield return SendJson(json); + yield return SendRequest("BalanceOf", new string[] { ExploadTypeConverters.PrintBytes(arg0) }, false); } } - class AllowanceRequest: ProgramRequest { - + public class AllowanceRequest: ProgramRequest + { public AllowanceRequest(byte[] programAddress) : base(programAddress) { } - protected override uint ParseResult(string json) + protected override long ParseResult(string elem) + { + return ExploadTypeConverters.ParseInt64(elem); + } + + public IEnumerator Test(byte[] arg0, byte[] arg1) + { + yield return SendRequest("Allowance", new string[] { ExploadTypeConverters.PrintBytes(arg0), ExploadTypeConverters.PrintBytes(arg1) }, true); + } + + public IEnumerator Call(byte[] arg0, byte[] arg1) { - return UintResult.FromJson(json).value; + yield return SendRequest("Allowance", new string[] { ExploadTypeConverters.PrintBytes(arg0), ExploadTypeConverters.PrintBytes(arg1) }, false); } + // Same as Call + // Deprecated public IEnumerator Allowance(byte[] arg0, byte[] arg1) { - String json = String.Format("{{ \"address\": {0}, \"method\": \"allowance\", \"args\": [{{ \"value\": {1}, \"tpe\": \"bytes\" }}, {{ \"value\": {2}, \"tpe\": \"bytes\" }}] }}", "\"" + BitConverter.ToString(ProgramAddress).Replace("-","") + "\"" , "\"" + BitConverter.ToString(arg0).Replace("-","") + "\"" , "\"" + BitConverter.ToString(arg1).Replace("-","") + "\"" ); - yield return SendJson(json); + yield return SendRequest("Allowance", new string[] { ExploadTypeConverters.PrintBytes(arg0), ExploadTypeConverters.PrintBytes(arg1) }, false); } } - class TransferRequest: ProgramRequest { - + public class TransferRequest: ProgramRequest + { public TransferRequest(byte[] programAddress) : base(programAddress) { } - protected override object ParseResult(string json) + protected override object ParseResult(string elem) { - return null; + return ExploadTypeConverters.ParseNull(elem); } - public IEnumerator Transfer(byte[] arg0, uint arg1) + public IEnumerator Test(byte[] arg0, long arg1) { - String json = String.Format("{{ \"address\": {0}, \"method\": \"transfer\", \"args\": [{{ \"value\": {1}, \"tpe\": \"bytes\" }}, {{ \"value\": {2}, \"tpe\": \"uint32\" }}] }}", "\"" + BitConverter.ToString(ProgramAddress).Replace("-","") + "\"" , "\"" + BitConverter.ToString(arg0).Replace("-","") + "\"" , arg1); - yield return SendJson(json); + yield return SendRequest("Transfer", new string[] { ExploadTypeConverters.PrintBytes(arg0), ExploadTypeConverters.PrintInt64(arg1) }, true); } - } - class ApproveRequest: ProgramRequest { + public IEnumerator Call(byte[] arg0, long arg1) + { + yield return SendRequest("Transfer", new string[] { ExploadTypeConverters.PrintBytes(arg0), ExploadTypeConverters.PrintInt64(arg1) }, false); + } + + // Same as Call + // Deprecated + public IEnumerator Transfer(byte[] arg0, long arg1) + { + yield return SendRequest("Transfer", new string[] { ExploadTypeConverters.PrintBytes(arg0), ExploadTypeConverters.PrintInt64(arg1) }, false); + } + } + public class ApproveRequest: ProgramRequest + { public ApproveRequest(byte[] programAddress) : base(programAddress) { } - protected override object ParseResult(string json) + protected override object ParseResult(string elem) { - return null; + return ExploadTypeConverters.ParseNull(elem); } - public IEnumerator Approve(byte[] arg0, uint arg1) + public IEnumerator Test(byte[] arg0, long arg1) { - String json = String.Format("{{ \"address\": {0}, \"method\": \"approve\", \"args\": [{{ \"value\": {1}, \"tpe\": \"bytes\" }}, {{ \"value\": {2}, \"tpe\": \"uint32\" }}] }}", "\"" + BitConverter.ToString(ProgramAddress).Replace("-","") + "\"" , "\"" + BitConverter.ToString(arg0).Replace("-","") + "\"" , arg1); - yield return SendJson(json); + yield return SendRequest("Approve", new string[] { ExploadTypeConverters.PrintBytes(arg0), ExploadTypeConverters.PrintInt64(arg1) }, true); + } + + public IEnumerator Call(byte[] arg0, long arg1) + { + yield return SendRequest("Approve", new string[] { ExploadTypeConverters.PrintBytes(arg0), ExploadTypeConverters.PrintInt64(arg1) }, false); } - } - class TransferFromRequest: ProgramRequest { + // Same as Call + // Deprecated + public IEnumerator Approve(byte[] arg0, long arg1) + { + yield return SendRequest("Approve", new string[] { ExploadTypeConverters.PrintBytes(arg0), ExploadTypeConverters.PrintInt64(arg1) }, false); + } + } + public class TransferFromRequest: ProgramRequest + { public TransferFromRequest(byte[] programAddress) : base(programAddress) { } - protected override object ParseResult(string json) + protected override object ParseResult(string elem) + { + return ExploadTypeConverters.ParseNull(elem); + } + + public IEnumerator Test(byte[] arg0, byte[] arg1, long arg2) + { + yield return SendRequest("TransferFrom", new string[] { ExploadTypeConverters.PrintBytes(arg0), ExploadTypeConverters.PrintBytes(arg1), ExploadTypeConverters.PrintInt64(arg2) }, true); + } + + public IEnumerator Call(byte[] arg0, byte[] arg1, long arg2) { - return null; + yield return SendRequest("TransferFrom", new string[] { ExploadTypeConverters.PrintBytes(arg0), ExploadTypeConverters.PrintBytes(arg1), ExploadTypeConverters.PrintInt64(arg2) }, false); } - public IEnumerator TransferFrom(byte[] arg0, byte[] arg1, uint arg2) + // Same as Call + // Deprecated + public IEnumerator TransferFrom(byte[] arg0, byte[] arg1, long arg2) { - String json = String.Format("{{ \"address\": {0}, \"method\": \"transferFrom\", \"args\": [{{ \"value\": {1}, \"tpe\": \"bytes\" }}, {{ \"value\": {2}, \"tpe\": \"bytes\" }}, {{ \"value\": {3}, \"tpe\": \"uint32\" }}] }}", "\"" + BitConverter.ToString(ProgramAddress).Replace("-","") + "\"" , "\"" + BitConverter.ToString(arg0).Replace("-","") + "\"" , "\"" + BitConverter.ToString(arg1).Replace("-","") + "\"" , arg2); - yield return SendJson(json); + yield return SendRequest("TransferFrom", new string[] { ExploadTypeConverters.PrintBytes(arg0), ExploadTypeConverters.PrintBytes(arg1), ExploadTypeConverters.PrintInt64(arg2) }, false); } } } \ No newline at end of file diff --git a/codegen/src/test/resources/OneMethod.generated.cs b/codegen/src/test/resources/OneMethod.generated.cs index 75d83d04..f72e5028 100644 --- a/codegen/src/test/resources/OneMethod.generated.cs +++ b/codegen/src/test/resources/OneMethod.generated.cs @@ -1,77 +1,33 @@ using System; using System.Collections; -using UnityEngine; -using UnityEngine.Networking; +using Expload.Unity.Codegen; -namespace Expload.Pravda.ERC20 { - [System.Serializable] - class UintResult { - public uint value; - public string tpe; - - public static UintResult FromJson(string json) { - return JsonUtility.FromJson(json); - } - } - - abstract class ProgramRequest +namespace Expload.Pravda.OneMethod +{ + public class BalanceOfRequest: ProgramRequest { - public byte[] ProgramAddress { get; protected set; } - - public T Result { get; protected set; } - public string Error { get; protected set; } - public bool IsError { get; protected set; } + public BalanceOfRequest(byte[] programAddress) : base(programAddress) { } - protected ProgramRequest(byte[] programAddress) + protected override long ParseResult(string elem) { - ProgramAddress = programAddress; - IsError = false; - Error = ""; + return ExploadTypeConverters.ParseInt64(elem); } - protected abstract T ParseResult(string json); - - protected IEnumerator SendJson(string json) + public IEnumerator Test(byte[] arg0) { - UnityWebRequest www = UnityWebRequest.Put("localhost:8087/api/program/method", json); - www.method = "POST"; - www.SetRequestHeader("Content-Type", "application/json"); - - yield return www.SendWebRequest(); - - if (www.isNetworkError || www.isHttpError) - { - IsError = true; - Error = www.error; - } - else - { - try - { - Result = ParseResult(www.downloadHandler.text); - } - catch (ArgumentException e) - { - IsError = true; - Error = "Invalid JSON: " + www.downloadHandler.text + "\n" + e.Message; - } - } + yield return SendRequest("BalanceOf", new string[] { ExploadTypeConverters.PrintBytes(arg0) }, true); } - } - - class BalanceOfRequest: ProgramRequest { - - public BalanceOfRequest(byte[] programAddress) : base(programAddress) { } - protected override uint ParseResult(string json) + public IEnumerator Call(byte[] arg0) { - return UintResult.FromJson(json).value; + yield return SendRequest("BalanceOf", new string[] { ExploadTypeConverters.PrintBytes(arg0) }, false); } + // Same as Call + // Deprecated public IEnumerator BalanceOf(byte[] arg0) { - String json = String.Format("{{ \"address\": {0}, \"method\": \"balanceOf\", \"args\": [{{ \"value\": {1}, \"tpe\": \"bytes\" }}] }}", "\"" + BitConverter.ToString(ProgramAddress).Replace("-","") + "\"" , "\"" + BitConverter.ToString(arg0).Replace("-","") + "\"" ); - yield return SendJson(json); + yield return SendRequest("BalanceOf", new string[] { ExploadTypeConverters.PrintBytes(arg0) }, false); } } } \ No newline at end of file diff --git a/codegen/src/test/scala/pravda/codegen/dotnet/BasicTests.scala b/codegen/src/test/scala/pravda/codegen/dotnet/BasicTests.scala index 420a66ea..76af333b 100644 --- a/codegen/src/test/scala/pravda/codegen/dotnet/BasicTests.scala +++ b/codegen/src/test/scala/pravda/codegen/dotnet/BasicTests.scala @@ -9,12 +9,12 @@ import scala.io.Source object BasicTests extends TestSuite { val tests = Tests { - 'one_method - { + 'OneMethod - { TestUtils.assertEqual( DotnetCodegen.generateMethods( - "ERC20", + "OneMethod", List( - Meta.MethodSignature("balanceOf", Meta.TypeSignature.Uint32, List(Meta.TypeSignature.Bytes)) + Meta.MethodSignature("BalanceOf", Meta.TypeSignature.Int64, List(Meta.TypeSignature.Bytes)) ) ), Source.fromResource("OneMethod.generated.cs").mkString @@ -26,20 +26,20 @@ object BasicTests extends TestSuite { DotnetCodegen.generateMethods( "ERC20", List( - Meta.MethodSignature("balanceOf", Meta.TypeSignature.Uint32, List(Meta.TypeSignature.Bytes)), - Meta.MethodSignature("allowance", - Meta.TypeSignature.Uint32, + Meta.MethodSignature("BalanceOf", Meta.TypeSignature.Int64, List(Meta.TypeSignature.Bytes)), + Meta.MethodSignature("Allowance", + Meta.TypeSignature.Int64, List(Meta.TypeSignature.Bytes, Meta.TypeSignature.Bytes)), - Meta.MethodSignature("transfer", + Meta.MethodSignature("Transfer", Meta.TypeSignature.Null, - List(Meta.TypeSignature.Bytes, Meta.TypeSignature.Uint32)), - Meta.MethodSignature("approve", + List(Meta.TypeSignature.Bytes, Meta.TypeSignature.Int64)), + Meta.MethodSignature("Approve", Meta.TypeSignature.Null, - List(Meta.TypeSignature.Bytes, Meta.TypeSignature.Uint32)), + List(Meta.TypeSignature.Bytes, Meta.TypeSignature.Int64)), Meta.MethodSignature( - "transferFrom", + "TransferFrom", Meta.TypeSignature.Null, - List(Meta.TypeSignature.Bytes, Meta.TypeSignature.Bytes, Meta.TypeSignature.Uint32) + List(Meta.TypeSignature.Bytes, Meta.TypeSignature.Bytes, Meta.TypeSignature.Int64) ) ) ), diff --git a/common/src/main/scala/pravda/common/bytes/package.scala b/common/src/main/scala/pravda/common/bytes/package.scala index 41a388eb..af1c17e0 100644 --- a/common/src/main/scala/pravda/common/bytes/package.scala +++ b/common/src/main/scala/pravda/common/bytes/package.scala @@ -18,6 +18,7 @@ package pravda.common import java.nio.charset.{Charset, StandardCharsets} +import java.util.Base64 import com.google.protobuf.ByteString @@ -167,4 +168,12 @@ package object bytes { new String(array, charset) } + def byteStringToBase64(byteString: ByteString): String = { + Base64.getEncoder.encodeToString(byteString.toByteArray) + } + + def hexToBase64(hex: String): String = { + Base64.getEncoder.encodeToString(hex2bytes(hex)) + } + } diff --git a/common/src/main/scala/pravda/common/json/TethysInstances.scala b/common/src/main/scala/pravda/common/json/TethysInstances.scala index fafb0d22..b3db1f78 100644 --- a/common/src/main/scala/pravda/common/json/TethysInstances.scala +++ b/common/src/main/scala/pravda/common/json/TethysInstances.scala @@ -102,7 +102,7 @@ trait TethysInstances { // scala.Map support //--------------------------------------------------------------------------- - trait MapKeySupport[T] { + trait MapKeySupport[T] { // TODO use KeyReader, KeyWriter instead def show(x: T): String def mk(x: String): T } diff --git a/common/src/test/scala/pravda/common/PreserveColoursFramework.scala b/common/src/test/scala/pravda/common/PreserveColoursFramework.scala new file mode 100644 index 00000000..6a6e1538 --- /dev/null +++ b/common/src/test/scala/pravda/common/PreserveColoursFramework.scala @@ -0,0 +1,6 @@ +package pravda.common +import utest.ufansi.Attrs + +class PreserveColoursFramework extends utest.runner.Framework { + override def exceptionMsgColor: Attrs = Attrs.Empty +} diff --git a/doc/ref/cli/main.md b/doc/CLI/index.md similarity index 82% rename from doc/ref/cli/main.md rename to doc/CLI/index.md index d134a4c6..b2e4cbf9 100644 --- a/doc/ref/cli/main.md +++ b/doc/CLI/index.md @@ -2,7 +2,7 @@ THIS FILE IS GENERATED. DO NOT EDIT MANUALLY! --> -```pravda [gen|run|compile|broadcast|node]``` +```pravda [gen|run|compile|broadcast|execute|node]``` ## Description Pravda Command Line Interface @@ -20,10 +20,12 @@ No options available |[`compile asm`](pravda-compile-asm.md)|Assemble Pravda VM bytecode from text representation. Input file is a Pravda assembly language text file. Output is binary Pravda program. By default read from stdin and print to stdout.| |[`compile disasm`](pravda-compile-disasm.md)|Disassemble Pravda VM bytecode to text presentation. Input file is a Pravda executable binary. Output is a text file with Pravda assembly code. By default read from stdin and print to stdout.| |[`compile dotnet`](pravda-compile-dotnet.md)|Compile .exe produced by .NET compiler to Pravda VM bytecode. Input file is a .NET PE (portable executable). Output is binary Pravdaprogram. By default read from stdin and print to stdout| +|[`compile evm`](pravda-compile-evm.md)|[THIS COMPILATION MODE IS EXPERIMENTAL]Compile .bin produced by solc compiler to Pravda VM bytecode. Input files are .bin contract and .abi. Output is binary Pravda program. By default read from stdin and print to stdout| |[`broadcast run`](pravda-broadcast-run.md)|Send a transaction with Pravda Program address to the blockchain to run it| |[`broadcast transfer`](pravda-broadcast-transfer.md)|Transfer native coins to a given wallet.| |[`broadcast deploy`](pravda-broadcast-deploy.md)|Deploy Pravda program to the blockchain.| |[`broadcast seal`](pravda-broadcast-seal.md)|Seal existing Pravda program in the blockchain.| |[`broadcast update`](pravda-broadcast-update.md)|Update existing Pravda program in the blockchain.| +|[`execute`](pravda-execute.md)|Executes program without side-effects. No watt-limit is required.| |[`node init`](pravda-node-init.md)|Create data directory and configuration for a new node.| |[`node run`](pravda-node-run.md)|Run initialized node.| \ No newline at end of file diff --git a/doc/ref/cli/pravda-broadcast-deploy.md b/doc/CLI/pravda-broadcast-deploy.md similarity index 100% rename from doc/ref/cli/pravda-broadcast-deploy.md rename to doc/CLI/pravda-broadcast-deploy.md diff --git a/doc/ref/cli/pravda-broadcast-run.md b/doc/CLI/pravda-broadcast-run.md similarity index 100% rename from doc/ref/cli/pravda-broadcast-run.md rename to doc/CLI/pravda-broadcast-run.md diff --git a/doc/ref/cli/pravda-broadcast-seal.md b/doc/CLI/pravda-broadcast-seal.md similarity index 100% rename from doc/ref/cli/pravda-broadcast-seal.md rename to doc/CLI/pravda-broadcast-seal.md diff --git a/doc/ref/cli/pravda-broadcast-transfer.md b/doc/CLI/pravda-broadcast-transfer.md similarity index 100% rename from doc/ref/cli/pravda-broadcast-transfer.md rename to doc/CLI/pravda-broadcast-transfer.md diff --git a/doc/ref/cli/pravda-broadcast-update.md b/doc/CLI/pravda-broadcast-update.md similarity index 100% rename from doc/ref/cli/pravda-broadcast-update.md rename to doc/CLI/pravda-broadcast-update.md diff --git a/doc/ref/cli/pravda-compile-asm.md b/doc/CLI/pravda-compile-asm.md similarity index 100% rename from doc/ref/cli/pravda-compile-asm.md rename to doc/CLI/pravda-compile-asm.md diff --git a/doc/ref/cli/pravda-compile-disasm.md b/doc/CLI/pravda-compile-disasm.md similarity index 100% rename from doc/ref/cli/pravda-compile-disasm.md rename to doc/CLI/pravda-compile-disasm.md diff --git a/doc/ref/cli/pravda-compile-dotnet.md b/doc/CLI/pravda-compile-dotnet.md similarity index 100% rename from doc/ref/cli/pravda-compile-dotnet.md rename to doc/CLI/pravda-compile-dotnet.md diff --git a/doc/CLI/pravda-compile-evm.md b/doc/CLI/pravda-compile-evm.md new file mode 100644 index 00000000..702ea63c --- /dev/null +++ b/doc/CLI/pravda-compile-evm.md @@ -0,0 +1,14 @@ + + +```pravda compile evm --input --output ``` + +## Description +[THIS COMPILATION MODE IS EXPERIMENTAL]Compile .bin produced by solc compiler to Pravda VM bytecode. Input files are .bin contract and .abi. Output is binary Pravda program. By default read from stdin and print to stdout +## Options + +|Option|Description| +|----|----| +|`-i`, `--input`|Input file +|`-o`, `--output`|Output file diff --git a/doc/CLI/pravda-execute.md b/doc/CLI/pravda-execute.md new file mode 100644 index 00000000..fdc35ea3 --- /dev/null +++ b/doc/CLI/pravda-execute.md @@ -0,0 +1,14 @@ + + +```pravda execute --wallet --endpoint ``` + +## Description +Executes program without side-effects. No watt-limit is required. +## Options + +|Option|Description| +|----|----| +|`-w`, `--wallet`|File with user wallet. You can obtain it using 'pravda gen address' command. Format: {"address": , "privateKey": } +|`-e`, `--endpoint`|Node endpoint (http://localhost:8080/api/public by default). diff --git a/doc/ref/cli/pravda-gen-address.md b/doc/CLI/pravda-gen-address.md similarity index 100% rename from doc/ref/cli/pravda-gen-address.md rename to doc/CLI/pravda-gen-address.md diff --git a/doc/ref/cli/pravda-gen-unity.md b/doc/CLI/pravda-gen-unity.md similarity index 54% rename from doc/ref/cli/pravda-gen-unity.md rename to doc/CLI/pravda-gen-unity.md index 23458e4d..e95315ee 100644 --- a/doc/ref/cli/pravda-gen-unity.md +++ b/doc/CLI/pravda-gen-unity.md @@ -2,7 +2,7 @@ THIS FILE IS GENERATED. DO NOT EDIT MANUALLY! --> -```pravda gen unity --dir --input --exclude-big-integer``` +```pravda gen unity --input ``` ## Description Generate auxiliary code to call program's methods from Unity @@ -10,6 +10,4 @@ Generate auxiliary code to call program's methods from Unity |Option|Description| |----|----| -|`-d`, `--dir`|Output directory for generated sources. |`-i`, `--input`|Input file with assembly. -|`--exclude-big-integer`|Exclude custom BigInteger class. diff --git a/doc/ref/cli/pravda-node-init.md b/doc/CLI/pravda-node-init.md similarity index 100% rename from doc/ref/cli/pravda-node-init.md rename to doc/CLI/pravda-node-init.md diff --git a/doc/ref/cli/pravda-node-run.md b/doc/CLI/pravda-node-run.md similarity index 100% rename from doc/ref/cli/pravda-node-run.md rename to doc/CLI/pravda-node-run.md diff --git a/doc/ref/cli/pravda-run.md b/doc/CLI/pravda-run.md similarity index 100% rename from doc/ref/cli/pravda-run.md rename to doc/CLI/pravda-run.md diff --git a/doc/codegen.md b/doc/codegen.md deleted file mode 100644 index 9e064d25..00000000 --- a/doc/codegen.md +++ /dev/null @@ -1,11 +0,0 @@ -# Code generation for Unity3D - -Pravda project is able to generate auxiliary code for [Unity](https://unity3d.com/) that provides convenient way to call _program's_ methods. -It uses [`meta`](ref/vm/meta.md) information from the given bytecode to detect and analyse methods of the _program_. - -## How to generate code - -``` -pravda gen unity --input input.pravda --dir output-dir/ -``` - diff --git a/doc/dev/add-new-functionality.md b/doc/contributing-guide/add-new-functionality.md similarity index 100% rename from doc/dev/add-new-functionality.md rename to doc/contributing-guide/add-new-functionality.md diff --git a/doc/dev/building-from-sources.md b/doc/contributing-guide/building-from-sources.md similarity index 85% rename from doc/dev/building-from-sources.md rename to doc/contributing-guide/building-from-sources.md index 30dd0454..a5e113e0 100644 --- a/doc/dev/building-from-sources.md +++ b/doc/contributing-guide/building-from-sources.md @@ -1,6 +1,6 @@ # Building from sources -Ensure that JDK 1.8 or higher is installer in your system. We use SBT native packager to produce runnable distros for each tool packed in +Ensure that JDK 1.8 or higher is installed in your system. We use SBT native packager to produce runnable distros for each tool packed in compressed archives. ``` diff --git a/doc/dev/gen-doc.md b/doc/contributing-guide/gen-doc.md similarity index 100% rename from doc/dev/gen-doc.md rename to doc/contributing-guide/gen-doc.md diff --git a/doc/dev/participation.md b/doc/contributing-guide/participation.md similarity index 100% rename from doc/dev/participation.md rename to doc/contributing-guide/participation.md diff --git a/doc/dev/add-new-functionality-dotnet.md b/doc/dev/add-new-functionality-dotnet.md new file mode 100644 index 00000000..40f2147f --- /dev/null +++ b/doc/dev/add-new-functionality-dotnet.md @@ -0,0 +1,108 @@ +# Adding new functionality to the DotNet translator + +The process consists of the following steps: + +1) Add stub method in [Pravda.cs](https://github.com/expload/pravda/blob/master/PravdaDotNet/Pravda.cs) without implementing any functionality +2) Describe translation of the method in [CallsTranslation](https://github.com/expload/pravda/blob/master/dotnet/src/main/scala/pravda/dotnet/translation/opcode/CallsTranslation.scala) +3) Write test C# program that uses that method. Place appropriate tests to [parser](https://github.com/expload/pravda/tree/master/dotnet/src/test/resources/parser), [translation](https://github.com/expload/pravda/tree/master/dotnet/src/test/resources/translation) and [teskit](https://github.com/expload/pravda/tree/master/testkit/src/test/resources) folders. + +## Stub method into Pravda.cs + +For example we want to add static method `ProgramAddress` to the `Info` class. In order to do it we can define stub method as following: + +``` +public static Bytes ProgramAddress() { return null; } +``` + +Returning value doesn't matter. You can return the default value for the returing type. + +## Method translation in CallsTranslation + +### Changing the stack during method execution + +We need to tell the translator how the stack is changed during method execution. In order to do it we should add pattern-match case in the `deltaOffsetOne` function similarly how it is done for other methods. + +For example `ProgramAddress()` method takes no parameters, but returns one value. It means, that height of the stack is changed by +1. If the method takes two parameters and returns one value, then height of the stack is changed by -1. + +### Translation of the method to the Pravda VM opcodes + +We need to add pattern-match for `Call` case with our method (similarly how it's done for other methods from [`Pravda.cs`](https://github.com/expload/pravda/blob/master/PravdaDotNet/Pravda.cs)) in the function `asmOpsOne`. There we need to implement the method using Pravda VM opcodes. + +For example for `ProgramAddress()` method we have `PADDR` opcode, so we can write the following: + +``` +case Call(MemberRefData(TypeRefData(_, "Info", "Expload.Pravda"), "ProgramAddress", _)) => + Right(List(Operation(Opcodes.PADDR))) +``` + +## Parser and translation tests + +At first, we need to add test C# program to `dotnet-tests/resources/` folder. + +### Parser + +We need to create a file with `.prs` extension in `dotnet/src/test/resources/parser`. File name can be chosen arbitrary. + +The file should contain `preconditions` which describe dotnet compilation steps: + +``` +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: .exe + sources: + - Pravda.dll + - dotnet-tests/resources/.cs + optimize: true +--- +``` + +The next command completes the test file with expected parsing result. + +``` +sbt dotnet/test:runMain pravda.dotnet.ParserSuite --overwrite +``` + +### Translation + +Create a file similarly to the described one above, but now file extension should be `.trs` and the file should be created in the `dotnet/src/test/resources/translation` folder. + +The next command completes the test file with expected translation result. + +``` +sbt dotnet/test:runMain pravda.dotnet.TranslationSuite --overwrite +``` + +## Adding tests to the testkit + +We need to add runtime test to the appropriate folder in the [testkit](https://github.com/expload/pravda/tree/master/testkit/src/test/resources). + +The test file should have `.sbox` extension and contain the following minimum set of the preconditions: + +``` +stack: + [utf8.name of method from test C# program] +storage: + utf8.init: "null" +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: .exe + sources: + - Pravda.dll + - dotnet-tests/resources/.cs + optimize: true +--- +``` + +As you can see it contains three preconditions: + +- method name from the test C# program, that should be put on the top of the stack +- program should be initialized, so we put the following key-value pair to the storage: key = "utf8.init", value = "null" +- dotnet compilation steps diff --git a/doc/gen/src/main/scala/GenDocs.scala b/doc/gen/src/main/scala/GenDocs.scala index 6325056f..15e6aba2 100644 --- a/doc/gen/src/main/scala/GenDocs.scala +++ b/doc/gen/src/main/scala/GenDocs.scala @@ -84,8 +84,8 @@ object GenDocs extends App { def genCliDocs(check: Boolean = false): ValidatedNel[String, Unit] = { - val outDir = new File("doc/ref/cli") - val mainPageName = "main.md" + val outDir = new File("doc/CLI") + val mainPageName = "index.md" val mainPage = ( new File(outDir, mainPageName), @@ -132,7 +132,7 @@ object GenDocs extends App { } writeToFile( - new File("doc/ref/vm/opcodes.md"), + new File("doc/virtual-machine/opcodes.md"), s"""$DoNotEditWarn |# Pravda VM opcodes |$view""".stripMargin, @@ -168,9 +168,7 @@ object GenDocs extends App { case Data.Type.Int8 => "int8" case Data.Type.Int16 => "int16" case Data.Type.Int32 => "int32" - case Data.Type.Uint8 => "uint8" - case Data.Type.Uint16 => "uint16" - case Data.Type.Uint32 => "uint32" + case Data.Type.Int64 => "int64" } def typesToName(types: Seq[Data.Type]) = { @@ -196,7 +194,7 @@ object GenDocs extends App { .map { case (name, content) => writeToFile( - new File(s"doc/ref/vm/stdlib/$name.md"), + new File(s"doc/standard-library/$name.md"), s"$DoNotEditWarn\n$content", check ) @@ -204,20 +202,21 @@ object GenDocs extends App { .toList .sequence_ - val stdlibRes = { - val stdlibTable = table("Name", "Description") { - StandardLibrary.All.toList.map { f => - List(s"[${f.name}](stdlib/${nameToFileName(f.name)}.md)", f.description) - } - } - writeToFile( - new File(s"doc/ref/vm/stdlib.md"), - s"$DoNotEditWarn\n$stdlibTable", - check - ) - } - - (docsRes, stdlibRes).mapN { case _ => () } +// val stdlibRes = { +// val stdlibTable = table("Name", "Description") { +// StandardLibrary.All.toList.map { f => +// List(s"[${f.name}](${nameToFileName(f.name)}.md)", f.description) +// } +// } +// writeToFile( +// new File(s"doc/standard-library/index.md"), +// s"$DoNotEditWarn\n$stdlibTable", +// check +// ) +// } + + docsRes + //(docsRes, stdlibRes).mapN { case _ => () } } val check = args.contains("--check") diff --git a/doc/getting-started.md b/doc/getting-started.md deleted file mode 100644 index 938db13f..00000000 --- a/doc/getting-started.md +++ /dev/null @@ -1,69 +0,0 @@ -# Getting started - -## Installation - -Ensure that JRE 1.8 or higher is installed in your system. Also ensure that 8080 TCP port is free. - -### Windows - -Download MSI installer from [releases page](https://github.com/expload/pravda/releases). Double click on it. Currently installer is unsigned. It leads to red alert during installation. Do not afraid: it's OK. - -### Linux and macOS - -We've prepared universal installation script. Just run this command in your terminal. - -``` -curl -sL https://git.io/pravda-for-unix | bash -``` - -## Run node - -Then we need to run local node. First of all lets build initial coin distribution config. - -```bash -pravda gen address -o my-wallet.json -``` - -This command will generate ED25519 key pair. It's a valid Pravda wallet. Now you can add an address to coin distribution config. - -```json -[ - { - "address": "address from my-wallet.json", - "amount": 1000000000 - } -] -``` - -Save this to `my-coin-distribution.json`. Now lets initialize node configuration. - -```bash -pravda node init --local --coin-distribution my-coin-distribution.json -``` - -Congratulations! The configuration is written to `pravda-data/node.conf` directory (also you can chose data directory using `--data-dir` key). Now let's run the node. - -```bash -pravda node run -``` - -Now you have our own Pravda network with one validator, and one billion coins on your account. Check out `http://localhost:8080/ui`. - -## Transfer coins - -You are very rich! Now you want to donate a part of your wealth to some poor guy. Let's generate wallet for him. - -```bash -pravda gen address -o another-wallet.json -``` - -Wallet for poor guy is created. Now let's copy his address and transfer some coins. - -```bash -pravda broadcast transfer \ - --wallet my-wallet.json \ - --to \ - --amount 1000000 -``` - -Now the poor guy is not so poor. We have done a good deed. diff --git a/doc/index.md b/doc/index.md new file mode 100644 index 00000000..5aa72386 --- /dev/null +++ b/doc/index.md @@ -0,0 +1,125 @@ +# Getting started + +## Installation + +Ensure that JRE 1.8 or higher is installed in your system. Also ensure that 8080 TCP port is free. + +### Windows + +Download MSI installer from [releases page](https://github.com/expload/pravda/releases). Double click on it. Currently installer is unsigned. It leads to red alert during installation. Do not afraid: it's OK. + +### Linux and macOS + +We've prepared universal installation script. Just run this command in your terminal. + +``` +curl -sL https://git.io/pravda-for-unix | bash +``` + +## Getting started with CLI + +### Run node + +Then we need to run local node. First of all lets build initial coin distribution config. + +```bash +pravda gen address -o my-wallet.json +``` + +This command will generate ED25519 key pair. It's a valid Pravda wallet. Now you can add an address to coin distribution config. + +```json +[ + { + "address": "address from my-wallet.json", + "amount": 1000000000 + } +] +``` + +Save this to `my-coin-distribution.json`. Now lets initialize node configuration. + +```bash +pravda node init --local --coin-distribution my-coin-distribution.json +``` + +Congratulations! The configuration is written to `pravda-data/node.conf` directory (also you can chose data directory using `--data-dir` key). Now let's run the node. + +```bash +pravda node run +``` + +Now you have our own Pravda network with one validator, and one billion coins on your account. Check out `http://localhost:8080/ui`. + +### Transfer coins + +You are very rich! Now you want to donate a part of your wealth to some poor guy. Let's generate wallet for him. + +```bash +pravda gen address -o another-wallet.json +``` + +Wallet for poor guy is created. Now let's copy his address and transfer some coins. + +```bash +pravda broadcast transfer \ + --wallet my-wallet.json \ + --to \ + --amount 1000000 +``` + +Now the poor guy is not so poor. Great job! + +## Write your Pravda Program + +### Importing Project Template + +To use project template you will need [.NET Core SDK 2.1](https://www.microsoft.com/net/download/dotnet-core/2.1). +Create a directory for your project and run inside it: +```bash +dotnet new -i Expload.PravdaProgramTemplate +dotnet new pravdaprogram --namespace MyTeam --version 1.0.0 --name HelloProgram +``` +Now your directory contains: + - `HelloProgram.cs` - main program file. By default it contains simple program with `HelloWorld` method + - `HelloProgram.csproj` - project configuration. It includes scripts for compiling and deployment, as well as .NET dependencies & metadata + - `README.md` - feel free to consult it whenever you feel confused + +### Compiling the Program + +To compile C# code into pravda binary run: +``` +dotnet publish -c Debug +``` +Now you have `MyProject.pravda` in your project directory. +You can use [local node](#Getting-started-with-CLI) and [Pravda CLI commands](https://github.com/expload/pravda/blob/master/doc/ref/cli/pravda-broadcast-deploy.md) to deploy to a local node or proceed further to work with Pravda Testnet. + +### Deploying to Testnet + +Let us generate a Pravda Wallet: + +```bash +pravda gen address -o wallet.json +``` + +If you already have a Pravda Wallet, move it to project folder and +rename it to `wallet.json`. + +Go to [Testnet Faucet](https://faucet.dev.expload.com/ui) to get some XCoin, +as you have to pay for deployment transaction. + +Finally, run: +``` +dotnet publish -c Deploy +``` +Now your program is on the Testnet! Your project directory +includes `program-wallet.json` which has your program's address. + +If you want to update program run + +```bash +dotnet publish -c Update +``` + +For more information on working with the template, +see `README.md` in your project directory. diff --git a/doc/integration/broadcaster.md b/doc/integration/broadcaster.md new file mode 100644 index 00000000..e316937e --- /dev/null +++ b/doc/integration/broadcaster.md @@ -0,0 +1,80 @@ +# Broadcaster + +The service provides REST API to send transactions to the network. Dedicated service is required because Pravda Node API takes already compiled bytecode with signature. It means you can't send transaction without knowledge of transaction binary format. This service can take Pravda Assembler, compile it, sign with given `PRAVDA_BROADCAST_SK` and send to `PRAVDA_BROADCAST_ENPOINT` endpoint. It allows to send transactions from any programing language which supports HTTP. + +### Install +Requires environment variables: + +* `PRAVDA_BROADCAST_ENPOINT` - URL to broadcast transactions. +* `PRAVDA_BROADCAST_PK` - public key of signer +* `PRAVDA_BROADCAST_SK` - private key of signer + +Run on docker: + +``` +docker run \ + -p 127.0.0.1:5000:5000 \ + -e PRAVDA_BROADCAST_PK= \ + -e PRAVDA_BROADCAST_SK= \ + -e PRAVDA_BROADCAST_ENDPOINT=https://publicnode.expload.com/api/public/broadcast \ + expload/pravda-broadcaster +``` + +### Request + +``` +curl --request POST \ + --url '/broadcast?wattLimit=&wattPrice=' \ + --header 'content-type: ' + ``` +### Response + +``` +{ + "executionResult" : { + "success" : { + "spentWatts" : , + "refundWatts" : , + "totalWatts" : , + "stack" : [], + "heap" : [] + } + }, + "effects" : [ { + "eventType" : "", + ... + + ... + }, ...] +} +``` +or with error + +``` +{ + "executionResult" : { + "failure" : { + "error" : { + "code": , + "message": , + }, + "finalState" : { + "spentWatts" : , + "refundWatts" : , + "totalWatts" : , + "stack" : [], + "heap" : [] + }, + "callStack" : [], + "lastPosition" : + } + }, + "effects" : [ { + "eventType" : "", + ... + + ... + }, ...] +} +``` \ No newline at end of file diff --git a/doc/integration/codegen.md b/doc/integration/codegen.md new file mode 100644 index 00000000..fafe4a93 --- /dev/null +++ b/doc/integration/codegen.md @@ -0,0 +1,57 @@ +# Code generation for Unity3D + +Pravda project is able to generate auxiliary code for [Unity](https://unity3d.com/) that provides convenient way to call _program's_ methods. +It uses [`meta`](../virtual-machine/meta.md) information from the given bytecode to detect and analyse methods of the _program_. + +## Dependecies +**Important note:** +The generated code uses [Json .NET](https://www.newtonsoft.com/json) library for hadling Json. +You can download Unity version of it from [AssetStore](https://assetstore.unity.com/packages/tools/input-management/json-net-for-unity-11347). + +## How to generate code + +``` +pravda gen unity --input input.pravda +``` + +This command will create `Assets` folder (if it doesn't exist) and will place several files in it. +You should include these files to your Unity project. + +## How to use generated code + +Generated code uses [coroutinues](https://docs.unity3d.com/ScriptReference/Coroutine.html) to handle [DApp API](dapp-api.md) requests. + +For example: if you have `Increment` method that takes integer and increment it in your program generated code will include `IncrementRequest` class +that handles DApp API request for calling `Increment` method. +```c# +var req = new IncrementRequest(address); // address of program in the blockchain as byte array +yield return req.Increment(42); +ProcessResult(req); +``` + +Internally `IncrementRequest` class will look like this: +```c# +public class IncrementRequest: ProgramRequest // int is the type of result +{ + public IncrementRequest(byte[] programAddress) : base(programAddress) { } // address of deployed program in the blockchain + + protected override int ParseResult(string elem) // method that parses Pravda specific format to result value + { + return ExploadTypeConverters.ParseInt32(elem); // parse int32 see (data specification)[ref/vm/data.md] + } + + public IEnumerator Increment(int arg0) + { + yield return SendRequest( // send http request to DApp API + "Increment", + new string[] { ExploadTypeConverters.PrintInt32(arg0) } // print int32 see (data specification)[ref/vm/data.md] + ); + } +} +``` + +## Expload.Unity.Codegen + +Internally generated code uses auxiliary functionality from namespace called `Expload.Unity.Codegen`. +Source file of this namespace situated [here](../../codegen/src/main/resources/ExploadUnityCodegen.cs). + diff --git a/doc/dapp-api.md b/doc/integration/dapp-api.md similarity index 59% rename from doc/dapp-api.md rename to doc/integration/dapp-api.md index 1a83478f..4c542310 100644 --- a/doc/dapp-api.md +++ b/doc/integration/dapp-api.md @@ -6,21 +6,18 @@ This API allows client to execute different action on the Pravda blockchain, such as run arbitrary code, call methods from existing programs, get balance by address, sign binary data and transfer money from one address to another. - -# General information - ## Login -Before using API, the user should be logged in app. User can sign up using its own public/private keys pair +Before using API the user should be logged in app. User can sign up using its own public/private keys pair or it can use automatically generated keys pair. -If the user has not logged, the `NoKeysError` will be returned for any API call. +If the user has not logged the `NoKeysError` will be returned for any API call. -To run the API, use `bin/expload-desktop` from http://download.expload.com/expload-desktop/. +To run the app use `bin/expload-desktop` from https://download.expload.com/expload-desktop/. ## API Endpoint -API server can be found at this address: `http://localhost:8087`. +API server can be found at `http://localhost:8087`. ## Response format @@ -43,7 +40,7 @@ The response should be a JSON object with the following structure: } ``` -If the `errorCode` field is empty, then it means no errors have happened and the `data` field contains the result. Otherwise, the `errorCode` has a predefined error code and the `error` field high likely contains error description. +If the `errorCode` field is empty then it means no errors have happened and the `data` field contains the result. Otherwise, the `errorCode` has a predefined error code and the `error` field contains error description. For example, if the user has not logged into the app, the caller should receive a response like this: @@ -62,25 +59,23 @@ Every API method may return error. If that happened the `errorCode` should not b Basic error codes are: -- `ActionNotConfirmedError` - means current user did not confirm the transaction +- `ActionNotConfirmedError` - means current user hasn't confirm the transaction - `NoKeysError` - means there is no current user, so there is no keys for signing transactions - `PravdaNodeHttpError` - means there is a connection problem with the Pravda Node. -- `UserErrorPravdaVmError` - means there was an error (like user-defined exception or system exception) in some Program happened +- `UserErrorPravdaVmError` - means there was an error (like user-defined exception or system exception) in some Program - `InsufficientFundsGameTokenProgramError` - means the user has not enough GameTokens -## JSON representation of the primitive data - -If you want to pass or receive values in JSON representation then you should follow -[this description](https://github.com/expload/pravda/blob/master/doc/ref/vm/data.md#json-representation) of how to do it. +## JSON representation of the data +If you want to handle values in JSON representation then consider the following +[description of data representation in JSON format](https://github.com/expload/pravda/blob/master/doc/ref/vm/data.md#json-representation). -# API Methods ## Get current user address -You can get the _current user_ address. The _current user_ is the user who able to sign a Pravda transaction by using its private key. +You can get the _current user_ address. The _current user_ is the user who is able to sign a Pravda transaction by using its private key. -If there is no current user (for instance, if the user has not logged into app) an API implemenation should return `NoKeysError` error. +If there is no current user (for instance, if the user has not logged into app) an API implementation should return `NoKeysError` error. ### Request @@ -108,7 +103,7 @@ should return: ## Get balance by Pravda address -You can get balance of the Pravda user by its address. Absent address parameter means the current user. +You can get balance of the Pravda user by its address. Blank address parameter indicates the current user`s address should be used. ### Request @@ -121,7 +116,7 @@ Requsted user balance as integer value ### Examples ``` -curl /api/address +curl /api/balance ``` will return balance of the current user. For instance, if the current user has 100 XCoins you will get @@ -135,20 +130,18 @@ the following result: } ``` -The same is equal for an arbitrary address. - ## Call program method DApps API Specification introduces __method__ entity. This entity is similar to [Solidity](http://solidity.readthedocs.io/en/v0.4.24/) methods from Ethereum ecosystem. DApp API specification establishes REST API for calling methods of the program with a given address. -An implementaion of the Standard should ask the current user if he confirms this transaction or not. -An implementaion of the Standard should show the user the programm address, program's method and arguments to be executed. +An implementation of the Standard should ask the current user if they confirm this transaction or not. +An implementation of the Standard should show the user the program address, program's method and arguments for the method. If this transaction is not confirmed, `ActionNotConfirmedError` error should be sent. Otherwise it should be signed with the current user private key and broadcasted to the Pravda blockchain. -If there is no current user, an implemenation of the Standard should return `NoKeysError` error. +If there is no current user, an implementation of the Standard should return `NoKeysError` error. ### Request @@ -158,11 +151,11 @@ If there is no current user, an implemenation of the Standard should return `NoK { "address": "", "method": "", - "args": [ ] + "args": [ ] } ``` -Arguments should have properly encoded according to their JSON representation (see corresponding section of that document). +Arguments should be properly encoded according to their [JSON representation](https://github.com/expload/pravda/blob/master/doc/ref/vm/data.md#json-representation). ### Response @@ -171,6 +164,7 @@ Arguments should have properly encoded according to their JSON representation (s "error: "", "errorCode: "", "data" : { + "transactionId": "", "finalState" : { "spentWatts" : , "refundWatts" : , @@ -190,24 +184,25 @@ Arguments should have properly encoded according to their JSON representation (s ### Examples -For example if we want to call `balanceOf` method for user with `0xABCDEF` address +For example if we want to call `BalanceOf` method for user with `0xABCDEF` address of some program with `0xe1941077e00b3cf81a8275788334292d9b2e2f0002bd622444cb37fa5e4d08a0` address we should pass: ``` curl -X POST -H "Content-Type: application/json" \ --data '{"address": "e1941077e00b3cf81a8275788334292d9b2e2f0002bd622444cb37fa5e4d08a0", - "method": "balanceOf", + "method": "BalanceOf", "args": ["bytes.ABCDEF"] }' /api/program/method ``` -As result we might receive the response like that: +As result we will receive the response like that: ``` { "error: "", "errorCode: "", "data" : { + "transactionId": "", "finalState" : { "spentWatts" : , "refundWatts" : , @@ -225,12 +220,20 @@ As result we might receive the response like that: } ``` -Balance can be found in "data \ finalState \ stack" field. +Balance is placed in `"data" \ "finalState" \ "stack"` field. +### Test/read requests + +Also you can call method without producing effects on blockchain. It may +be useful when you want to require some data from storage of a program. + +`POST api/program/method-test` + +Everything else is a same. ## Transfer money -This method allows you to transfer money from current user to any Pravda address +This method allows you to transfer XCoins from current user to any Pravda address ### Request @@ -238,8 +241,8 @@ This method allows you to transfer money from current user to any Pravda address ``` { - "to": "", - "amount": + "to": "", + "amount": } ``` @@ -255,7 +258,7 @@ curl -X POST -H "Content-Type: application/json" --data '{ "to": "e1941077e00b3c ## Execute VM bytecode -This method allows you to execute any Pravda bytecode +This method allows you to execute arbitrary Pravda bytecode ### Request @@ -263,7 +266,7 @@ This method allows you to execute any Pravda bytecode ``` { - "transaction": "", + "program": "", "wattLimit": // Optional } ``` @@ -272,51 +275,41 @@ This method allows you to execute any Pravda bytecode ``` { - "finalState" : { - "spentWatts" : , - "refundWatts" : , - "totalWatts" : , - "stack" : [], - "heap" : [] - }, - "effects" : [ { - "eventType" : "", - ... - - ... - }, ...] + "error: "", + "errorCode: "", + "data" : { + "transactionId": "", + "finalState" : { + "spentWatts" : , + "refundWatts" : , + "totalWatts" : , + "stack" : [], + "heap" : [] + }, + "effects" : [ { + "eventType" : "", + ... + + ... + }, ...] + } } ``` or with error ``` { - "executionResult" : { - "error" : { - "error" : , - "finalState" : { - "spentWatts" : , - "refundWatts" : , - "totalWatts" : , - "stack" : [], - "heap" : [] - }, - "callStack" : [], - "lastPosition" : - } - }, - "effects" : [ { - "eventType" : "", - ... - + "error: "", + "errorCode: "", + "data" : { ... - }, ...] + } } ``` ## Sign binary data -This method allows you to execute any Pravda bytecode +This method allows you to sign arbitrary binary data ### Request @@ -324,7 +317,7 @@ This method allows you to execute any Pravda bytecode ``` { - "app": "", + "app": "", "bytes": "" } ``` @@ -332,11 +325,10 @@ This method allows you to execute any Pravda bytecode ### Response ``` { - "signedBytes": "" + "error": "", + "errorCode": "", + "data": { + "signedData": "" + } } ``` - -### Additional information - -You can also sign binary data with `application/octet-stream` or `application/base64` -http content types by using `api/binary/sign?app=` url with corresponding type. diff --git a/doc/node-api.md b/doc/integration/node-api.md similarity index 81% rename from doc/node-api.md rename to doc/integration/node-api.md index 16849d61..8adda2c0 100644 --- a/doc/node-api.md +++ b/doc/integration/node-api.md @@ -1,3 +1,4 @@ + # Node API Node provides REST API to access the current blockchain status. @@ -7,7 +8,17 @@ You can get the generated events by name and the address of the program where th ### Request -`GET api/events?address=&name=[&offset=&count=]` +`GET api/events` + +#### Parameters + +|Parameter|Optional|Type| +|---|--|---| +|program|No|hex| +|name|No|string| +|transactionId|Yes|hex| +|offset|Yes|integer| +|count|Yes|integer| ### Response @@ -16,10 +27,12 @@ The list of data from events with given name at the given address: [ { "data": "", + "transactionId": 0000000, "offset": 0 }, { "data": "", + "transactionId": 1111111, "offset": 1 } ... diff --git a/doc/pravda-arch.svg b/doc/pravda-arch.svg deleted file mode 100644 index 9f012532..00000000 --- a/doc/pravda-arch.svg +++ /dev/null @@ -1,443 +0,0 @@ - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - P2PNetwork - PravdaNode - Tendermintcore - ABCIServer - PravdaVM - - UIEndpoint - APIEndpoint - - - - PravdaNode - Tendermintcore - ABCIServer - PravdaVM - UIEndpoint - APIEndpoint - - - - - diff --git a/doc/ref/dotnet/classes-translation.md b/doc/ref/dotnet/classes-translation.md deleted file mode 100644 index ab990669..00000000 --- a/doc/ref/dotnet/classes-translation.md +++ /dev/null @@ -1,82 +0,0 @@ -# Dotnet classes translation - -### Program class - -Each C# file that is going to be translated should follow the listed rules: - * Class with Program methods must be marked with `[Program]` attribute - * Each compiled .exe file must contain exactly one class with `[Program]` attribute. - * Program class can inherit interfaces _[future plans]_ - * Program class contains only public fields and public or private methods. - * Static fields and methods are not allowed. - * Public methods are translated to Program methods. - * Private methods are translated to inner functions, only difference between them and Program methods is that inner functions are not accessible from outside world and can only be called from Program method. - * Fields (that can be only public) are translated to storage items with `utf8("p_")` keys. - * Only one constructor of Program class is allowed. This constructor mustn't have any arguments. - -### User defined classes - -User can freely define classes without `[Program]` attribute. -Objects of these classes are translated to [data `struct`s](../vm/data.md) and behave very similar to objects in C#. - -Formally this translation follow the listed rules: - * Class doesn't have `[Program]` attribute, `[Program]` attribute is used for only one class, which is translated to Program methods. - * Interfaces are not translated, they serve only as compile-time entities. - * **All** (`private`, `protected`, `internal` and `public`) fields are translated to `utf8("") -> ` pairs in `struct`. - * **All** methods are translated to `utf8("_")` -> `ref(#)`. - The `` prefix is needed to support overloading and - `` means offset of function that should be called for the `` method of that class. - Overridden methods will point to different ``. - This technique is similar to [Virtual method table](https://en.wikipedia.org/wiki/Virtual_method_table). - * `static` fields are translated to storage items with `utf8("s__")` keys. - * `static` methods are translated to inner functions with `__` names. - * Constructors are translated to inner functions with `_ctor_` names. - -##### Example: - -Suppose we have the following classes definitions in C#: -```c# -interface Vehicle -{ - void ComeIn(String someone); -} - -class Bicycle : Vehicle -{ - public String Owner = "no one"; - - void ComeIn(String someone) { - Owner = someone; - } -} - -class Car : Vehicle -{ - static private bool isBearBurnedDown = false; - static public bool IsBearBurnedDown() - { - return Car.isBearBurnedDown; - } - - public int NumberOfTires; - - public Car(int tires) - { - NumberOfTires = tires. - } - - void ComeIn(String someone) - { - if (someone == "bear") { - Car.isBearBurnedDown = true; - } - } -} -``` - -- `interface Vehicle` won't be translated to anything -- `Bicycle()` constructor will be translated to `Bicycle_ctor` function that creates `struct(utf8("Owner") -> utf8("no one"), utf8("ComeIn_string") -> ref(#)` -- `Bicycle.ComeIn` will be translated to some function (let's call it `function1`) that changes `utf8("Owner")` field in the given `struct`. -- `Car(int)` constructor will be translated to `Car_ctor_int32` function that creates `struct(utf8("NumberOfTires") -> int32(), utf8("ComeIn_string") -> ref(#))` -- `isBearBurnedDown` static field will be translated to `utf8("s_Car_isBearBurnedDown") -> bool` storage item -- `IsBearBurnedDown` static method will be translated to `Car_IsBearBurnedDown` function that reads `utf8("s_Car_isBearBurnedDown")` storage key. -- `Car.ComeIn` will be translated to some function (let's call it `function2`) that changes `utf8("s_Car_isBearBurnedDown") -> bool` storage item according to the given `String` from the stack. \ No newline at end of file diff --git a/doc/ref/dotnet/translation.md b/doc/ref/dotnet/translation.md deleted file mode 100644 index 4f41163b..00000000 --- a/doc/ref/dotnet/translation.md +++ /dev/null @@ -1,72 +0,0 @@ -# Dotnet Translation - -Pravda project allows you to write _programs_ in subset of C\# language. -Pravda Translator translates [CIL](https://en.wikipedia.org/wiki/Common_Intermediate_Language) to Pravda bytecode. - -## How to compile program -Pravda provides special `Pravda.dll` file with auxiliary methods for translation from CIL to Pravda bytecode. - -This dll file serves **only as meta info** for translator, -it __doesn't__ provide any meaningful implementation for these methods. -Translator just looks at calls of these methods and generates necessary Pravda bytecode. - -You can download `Pravda.dll` [here](../../../PravdaDotNet/Pravda.dll). -Source of this dll can be found [here](../../../PravdaDotNet/Pravda.cs). - -For full support of all translation features you need also to compile your program with `/debug:portable` option. -This options will trigger the creation of `your_program.pdb` file that contains various auxiliary information about C# source. - -_Portable_ pdb files are quite new, so you need up-to-date `csc` compiler to generate them. See more [here](https://github.com/dotnet/core/blob/master/Documentation/diagnostics/portable_pdb.md). - -To compile your C# program with [`Pravda.dll`](../../../dotnet-tests/resources/expload.dll): -```bash -csc your_program.cs /reference:Pravda.dll /debug:portable -``` - -## How to run translation - -Pravda CLI has special command to run translation of `.exe` file produced by C# compiler. -``` -pravda compile dotnet --input input.exe --output output.pravda --pdb input.pdb -``` -`pdb` file is optional, but it's strongly recommended to provide it (see [Compile](#how-to-compile-program) section for instructions). - -## Supported subset of C# - -Pravda Translator supports only part of all C# features. - -For the moment it supports the following: -- Access to the _storage_ via class fields; -- Access to the _storage_ via `Mapping` (`get`, `getDefault`, `put`, `exists` methods); -- Access to sender address via `Info.Sender()` method; -- Class methods that are translated to program methods; -- Integer primitive types (`int`, `short`, `byte`, `uint`) and `bool`; -- Basic arithmetics and logical operations; -- Local variables and method arguments; -- If conditions and loops; -- `String`s and auxiliary methods (`+`, access to particular chars, `Slice`); -- `Bytes` (immutable byte arrays), auxiliary methods (access to particular bytes, `Slice`, `Concat`), creation from `byte[]`: `new Bytes(bytes_array)`; -- Arrays of primitive types (`int`, `byte`, `String`), reading and writing of particular elements; -- Explicit conversion of primitive types via -`System.Convert.ToByte`, `System.Convert.ToChar`,`System.Convert.ToInt16`,`System.Convert.ToInt32`,`System.Convert.ToDouble`,`System.Convert.ToBoolean`,`System.Convert.ToString` -- Cryptographic functions: Ripemd160 hashing, validation of Ed25519 Signature. See more in [Standard library](../vm/stdlib.md) docs. -- User defined classes (although you can't store them in the storage yet). -- Calling other programs via `ProgramHelper.Program<...>` interface. -See some examples ([pcall.cs](../../../dotnet-tests/resources/pcall.cs), [pcall_program.cs](../../../dotnet-tests/pcall_program.cs)). -**Important note:** For being able to use `ProgramHelper.Program<...>` interface you should put called program to `Expload.Pravda.Programs` namespace. -- Create events in your program via `Log.Event("name of event", )`, see [event.cs](../../../dotnet-tests/resources/event.cs) - -Things that are *not* supported: -- Standard C# library (except of some specific functions from the list above); -- Standard C# collections. - -## Examples - -You can look at several examples of test _programs_ to learn current abilities of translation: -- [String examples](../../../dotnet-tests/resources/strings.cs) that show how to operate with `String`s. -- [Array examples](../../../dotnet-tests/resources/arrays.cs) that show how to operate with arrays. -- [Simple _program_](../../../dotnet-tests/resources/smart_program.cs) with `balanceOf` and `transfer` methods similar to corresponding methods from [ERC20](https://theethereum.wiki/w/index.php/ERC20_Token_Standard) -- [Buffer](../../../testkit/src/test/resources/buffer.cs) -- Dynamic resizable array implemented in C#. -- [Zoo _program_](../../../dotnet-tests/resources/zoo_program.cs) that allows you to create zoos, pets and breed them. -- [Poker _program_](../../../testkit/src/test/resources/poker.cs) that implements simple poker game on the blockchain. _(poker.cs was provided by [Ducatur team](https://github.com/DucaturFw/ExploadHackathonContract))_ - diff --git a/doc/ref/vm/meta.md b/doc/ref/vm/meta.md deleted file mode 100644 index e7f85620..00000000 --- a/doc/ref/vm/meta.md +++ /dev/null @@ -1,22 +0,0 @@ -# Meta information in Pravda bytecode - -Pravda bytecode may contain additional information for disassembler, code generator and other tools that read and interpreter Pravda bytecode. -This information is called `meta`. - -## Meta - -There're 5 kinds of `meta` for the moment: -- `label_def ""` that marks definition of a label; -- `label_use ""` that marks usage of a label; -- `program_name ""` that contains a name of a _program_; -- `method ` (see `struct` definition in [data spec](data.md)). More about this in the next chapter. -- `custom ""` that contains arbitrary information. - -### Method meta -`` in `method` must contain two pairs `int8(-1): ` and -`int8(-2): int8()` (See definition of `type` in [data spec](data.md)). - - -All other pairs in `` describe arguments of the method. -They packed in the following way: `int8(2 * i): int8()`, `int8(2 * i + 1): utf8()`. -Name of the argument is optional, pair with `int(2 * i + 1)` key may not exist even if there's `int(2 * i)` key. \ No newline at end of file diff --git a/doc/ref/vm/stdlib.md b/doc/ref/vm/stdlib.md deleted file mode 100644 index eb5b5ad8..00000000 --- a/doc/ref/vm/stdlib.md +++ /dev/null @@ -1,9 +0,0 @@ - -Name |Description -----------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------- -[Ripemd160](stdlib/ripemd160.md) |Calculate RIPEMD-160 hash for message. See https://homes.esat.kuleuven.be/~bosselae/ripemd160.html -[ValidateEd25519Signature](stdlib/validate-ed25519-signature.md)|Validates message signed with Ed25519 algorithm. See https://ed25519.cr.yp.to -[ExponentialFunction](stdlib/exponential-function.md) |Takes two items from the stack, raises the second number to a power of first number and pushes the result to the stack. -[HexToBytes](stdlib/hex-to-bytes.md) |Takes hex string from stack, pushes bytes to the stack \ No newline at end of file diff --git a/doc/spec.tex b/doc/spec.tex deleted file mode 100644 index b96a87d7..00000000 --- a/doc/spec.tex +++ /dev/null @@ -1,260 +0,0 @@ -\documentclass[12pt,a4paper]{article} - -\usepackage[utf8]{inputenc} -\usepackage{dcolumn} -\usepackage{tabularx} -\usepackage{scrextend} -\usepackage{url} -\usepackage{amsmath} - -\title{Pravda Virtual Machine} -\author{Aleksey Fomkin, Vasiliy Pankratov} -\date{\today} - -\begin{document} - -\maketitle - -\section{Introduction} - -THIS SPEC IS OUTDATED - -As part of the Expload Project, Pravda VM is designed to solve challenges of the game industry in section of decentralization. The purpose of Pravda VM is programmatic management of global state of the network. We're offering minimalistic deterministic virtual machine and runtime library for managed execution of smart-contracts. - -In our work we're focusing on simplicity and extensibility. We don't set ourselves a task to invent something new, but using solid foundation of computer science we consider requirements of the network. - -This document specifies memory and execution model, bytecode format, start-contracts interop and definition of standard library (including procedures and data types). Also this document defines gas-economy. The document \textit{doesn't} consider issues of blockchain, consensus, network of storage. - -\section{Execution model} - -\subsection{General} - -Pravda VM is a stack-based virtual machine. All data is represented by string, so "machine word" is a string. Pravda VM can store words in a managed heap. Heap is inaccessible for raw reads/writes and fully managed by runtime. Heap has map-like interface where key is string of four bytes and value is an arbitrary string. - -Persistence is an integral part of Pravda VM which is a representation of a global state of the network. Persistent state has map-like interface as the heap. The difference is programmer can use arbitrary string as a key. - -Code can be executed. -\subsection{Execution scopes} - -Launched code is named execution. There are two execution scopes: transaction and smart-contract. If launched code is placed in a transaction it has \textit{transaction scope}. Similarly if launched code is placed in a smart-contract it has \textit{smart-contract scope}. - -Execution in transactions scope has not persistent storage but has its own stack and heap. Execution in smart-contacts scope inherits both stack and heap from the execution which launches it. Also it has persistent storage shared between all the executions of the smart-contract. - -\subsection{Transaction isolation} - -As noted above persistence is an integral part of Pravda VM. Executions can read and write to the persistent storage. Pravda VM supports parallel execution of the same smart-contacts. It means concurrent access to the stored data. According to this requirement we need to provide highest level of consistency so transactions is isolated serializable\cite{transaction-isolation}. - -\section{Bytecode} - -BVM executes bytecode in the format described below. Every program is a linear sequence of \textit{opcodes.md}. Any opcode is represented by one byte. Exception is "PUSHX" opcode which is represented by one byte for opcode itself and a word to represent a constant. - -First 3 bits of the word encode length. This is the length of the length of the encoded data. For example, $256$ bytes of the data are encoded by the word of $258$ bytes , which are - -$$\underbrace{010}_{3~bits}\underbrace{00001~00000000}_{5~bits~+~1~byte} \underbrace{\cdots}_{256~bytes} $$ - -If the length of the length is 0 the data is encoded by the rest $5$ bits of the byte. - -\subsection{Control} -\begin{tabularx}{\textwidth}{ c l c c p{7cm} } -\textbf{Code} & \textbf{Mnemonic} & \textbf{$\Uparrow$} & \textbf{$\Downarrow$} & \textbf{Description} \\ -\hline -0x00 & STOP & $0$ & $0$ & Stops current execution. \\ -\hline -0x01 & JUMP & $1$ & $0$ & Alters program execution counter to value of first item of the stack. \\ -\hline -0x02 & JUMPI & $2$ & $0$ & If value in head of stack is greater than 0 then alters program execution counter to value of second item in the stack. \\ -\hline -0x03 & RUN & $1$ & $0$ & Launches smart-contract from address given in the first item of the stack. \\ -\hline -0x04 & CALL & $1$ & $0$ & Firstly, it pushes current program counter to the separate stack (so called \emph{call stack}). Then it alters program execution counter to the value of the first item of the stack. \\ -\hline -0x05 & RET & $0$ & $0$ & Alters program execution counter to the value of the first item of the call stack (see CALL opcode). \\ -\hline -0x06 & PCALL & $0$ & $0$ & Takes two words by which it is followed. They are address $a$ and the number of parameters $n$, respectively. Then it executes the smart contract with the address $a$ and passes there only $n$ top elements of the stack. \\ -\hline -0x07 & LCALL & $0$ & $0$ & Takes three words by which it is followed. They are address $a$, function $f$ and the number of parameters $n$, respectively. Then it executes the function $f$ of the library (which is a special form of smart contract) with the address $a$ and passes there only $n$ top elements of the stack. \\ -\hline -0x08 & PCREATE & $1$ & $1$ & Takes bytecode of a new program, put's it to state and returns programm address \\ -\hline -0x09 & PUPDATE & $2$ & $0$ & Takes address of a program and new bytecode. Replaces bytecode in storage. This opcode can be performed only from owner of the program \\ -\hline - -\end{tabularx} - -\subsection{Stack} -\begin{tabularx}{\textwidth}{ c l c c p{7cm} } -\textbf{Code} & \textbf{Mnemonic} & \textbf{$\Uparrow$} & \textbf{$\Downarrow$} & \textbf{Description} \\ -\hline -0x10 & POP & $1$ & $0$ & Removes first item from the stack. \\ -\hline -0x11 & PUSHX & $0$ & $1$ & Pushes the word following the opcode to the stack. \\ -\hline -0x12 & SLICE & $1$ & $1$ & Takes two words by which it is followed - $i$ and $j$ parameters. It pushes a part of the top word starting from $i$-th byte and ending with $j$-th byte. \\ -\hline -0x13 & CONCAT & $2$ & $1$ & Takes two top words from the stack and pushes their concatenation to the stack \\ -\hline -0x22 & DUP1 & $1$ & $2$ & Duplicates first item of the stack. \\ -\vdots & \vdots & \vdots & \vdots & \vdots \\ -0x31 & DUP16 & $1$ & $2$ & Duplicates sixteenth item of the stack. \\ -\hline -0x32 & DUPN & $1$ & $1$ & Duplicates $(n+1)$-th item of the stack where $n$ is the first item in stack. \\ -\hline -0x33 & SWAP1 & $2$ & $2$ & Swaps first two items in the stack. \\ -\vdots & \vdots & \vdots & \vdots & \vdots \\ -0x42 & SWAP16 & $2$ & $2$ & Swaps first and sixteenth items in the stack. \\ -\hline -0x43 & SWAPN & $1$ & $1$ & Swaps the second item of the stack with the $(n+1)$-th item of the stack where $n$ is first item in the stack. \\ -\hline -\end{tabularx} - -\subsection{Heap} -\begin{tabularx}{\textwidth}{ c l c c p{7cm} } -\textbf{Code} & \textbf{Mnemonic} & \textbf{$\Uparrow$} & \textbf{$\Downarrow$} & \textbf{Description} \\ -\hline -0x46 & MPUT & $1$ & $1$ & Saves first item in stack to the heap. Pushes address of the item to the heap. \\ -\hline -0x47 & MGET & $1$ & $1$ & Pushes an item saved in heap corresponding to address given in first item of the stack. \\ -\hline -\end{tabularx} - -\subsection{Persistent storage} -\begin{tabularx}{\textwidth}{ c l c c p{7cm} } -\textbf{Code} & \textbf{Mnemonic} & \textbf{$\Uparrow$} & \textbf{$\Downarrow$} & \textbf{Description} \\ -\hline -0x50 & SPUT & $2$ & $0$ & Saves the first item in the stack to the persistent storage using the second item of the stack as the address. \\ -\hline -0x51 & SGET & $1$ & $1$ & Pushes an item saved in persistent storage corresponding to address given in a first item of the stack. \\ -\hline -0x52 & SDROP & $1$ & $0$ & Removes item corresponding to address given in a first item of the stack from the persistent storage. \\ -\hline -\end{tabularx} - -\subsection{Int32 operations} -\begin{tabularx}{\textwidth}{ c l c c p{7cm} } -\textbf{Code} & \textbf{Mnemonic} & \textbf{$\Uparrow$} & \textbf{$\Downarrow$} & \textbf{Description} \\ -\hline -0x60 & I32ADD & $2$ & $1$ & Add two int32 from the stack. \\ -\hline -0x61 & I32MUL & $2$ & $1$ & Multiply int32 decimals from the stack. \\ -\hline -0x62 & I32DIV & $2$ & $1$ & Divide two int32 from the stack. \\ -\hline -0x63 & I32MOD & $2$ & $1$ & Finds the remainder after division of two int32 from the stack. \\ -\hline -0x67 & I32GT & $2$ & $1$ & Defines whether the top element of the stack is greater than the second. \\ -\hline -0x68 & I32LT & $2$ & $1$ & Defines whether the top element of the stack is less than the second. \\ -\hline - -\end{tabularx} - -\subsection{Logical operations} -\begin{tabularx}{\textwidth}{ c l c c p{7cm} } -\textbf{Code} & \textbf{Mnemonic} & \textbf{$\Uparrow$} & \textbf{$\Downarrow$} & \textbf{Description} \\ -\hline -0x80 & NOT & $1$ & $1$ & Negates the top element of the stack. \\ -\hline -0x81 & AND & $2$ & $1$ & Calculates "logical and" operation for top two elements. \\ -\hline -0x82 & OR & $2$ & $1$ & Calculates "logical or" operation for top two elements. \\ -\hline -0x83 & XOR & $2$ & $1$ & Calculates "logical xor" operation for top two elements. \\ -\hline -0x84 & EQ & $2$ & $1$ & Defines whether the top element is equal to the second. \\ -\hline -\end{tabularx} - -\subsection{Specials} -\begin{tabularx}{\textwidth}{ c l c c p{7cm} } -\textbf{Code} & \textbf{Mnemonic} & \textbf{$\Uparrow$} & \textbf{$\Downarrow$} & \textbf{Description} \\ -\hline -0xa0 & FROM & $1$ & $0$ & Gives current executor address. \\ -\hline -0xa1 & HEIGHT & $1$ & $0$ & Gives current block height. \\ -\hline -0xa2 & PADDR & $0$ & $0$ & Gives current program address. \\ -\hline -0xa3 & TRANSFER & $2$ & $0$ & Gets two parameters $a$ and $n$ from the stack and transfers $n$ native coins from the executor account to the account $a$. \\ -\hline -0xa4 & PTRANSFER & $2$ & $0$ & Gets two parameters $a$ and $n$ from the stack and transfers $n$ native coins from the current program account to the account $a$ . \\ -\hline - -\end{tabularx} - -\section{Watts} - -Everything has a price. When you send your program to the Pravda blockchain, validators need to spend their resources (like electricity, disk space and memory) just to execute it. The more complex calculations you execute, the more validator's resources are needed. So, you should give them some kind of money for your program. Amount of \emph{Watts} is the representaion of spent resources in Pravda. - -There are three main resources you use in Pravda - CPU time, memory and storage space. So, all of them has a cost in Watts, which are described below. - -\emph{Watt price} $W_p$ is the cost of one spent watt. The more price you assign to the transaction the more probability that your transaction will be chosen by the validators. - -\emph{Watt limit} $W_l$ is the limit of watts you think should be enough to execute your program. If it is actually not enough you will get OutOfWatts exception, your program will do nothing and you will pay for that transaction. - -Before you execute a transaction, a fee of $W_p \cdot W_l$ Native Coins will be charged from your account. After the execution is complete, you will get back $W_p \cdot \theta$, where $\theta = W_l - W_s + \min(W_r, \frac{W_s}{2})$. Here $W_s$ - spent Watts and $W_r$ - refund Watts. - -All the rest Native Coins, which are $W_p \cdot (W_l - \theta)$, are shared among the validators after the block containing the transaction is committed. - -\subsection{CPU time} - -Every new opcode needs 1 Watt to be executed. - -Every time you use storage we need time to read or write to it. So, it costs 20 Watts. - -$$ -C_s = \begin{cases} - 20 & \text{if storage is used} \\ - 0 & \text{otherwise} - \end{cases} -$$ - - -There are several groups of operations that needs different omount of CPU time and therefore different amount of watts. They are: - -$$ -C_{extra} = \begin{cases} - 5 & \text{for opcodes.md I32ADD,I32LT,I32GT,NOT,AND,OR,XOR,EQ} \\ % SimpleArithmetic - 5 & \text{for opcodes.md CALL,JUMP} \\ % Control - 10 & \text{for opcodes.md I32DIV,I32MUL,I32MOD,FDIV,FMUL,FMOD} \\ % Arithmetic - 10 & \text{for opcodes.md PCALL,LCALL} \\ % ExtCall - 15 & \text{for opcodes.md JUMPI} \\ - 0 & \text{for other opcodes.md} \\ - \end{cases} -$$ - -CPU time watts for every opcode are calculates as: -$$ C_{op} = 1 + C_{s} + C_{extra} $$ - - -So, total CPU time watts for the transaction are calculates as: - -$$ C_w = \sum_{op} C_{op} $$ - -\subsection{Memory} - -Memory in pravda consists of two main parts - stack and heap. PUSHX opcode allocates stack memory and MPUT opcode allocates heap memory. Pravda calculates maximum amount of bytes $b_m$ you spent during the transaction execution. Memory watts are calculated as: - -$$M_w = 100\cdot(\lfloor b_m/100,000,000 \rfloor + 1)^3 $$ - -\subsection{Storage} - -Every time you put something into the storage using SPUT or SDROP opcodes.md, pravda calculates amount of bytes you occupy $o_s$ and amount of bytes you release $r_s$. Total storage watts are calculates as: - -$$S_w = 8 \cdot o_s $$ - -Refund is calculates as: -$$W_r = 2 \cdot r_s $$ - -\subsection{Total} - -Total amount of spent watts is calculates as: - -$W_s = C_w + M_w + S_w$ - -\begin{thebibliography}{9} - -\bibitem{transaction-isolation} Understanding Isolation Levels \url{https://docs.microsoft.com/en-us/sql/connect/jdbc/understanding-isolation-levels} -\end{thebibliography} - -\end{document} - diff --git a/doc/standard-library/bytes-to-hex.md b/doc/standard-library/bytes-to-hex.md new file mode 100644 index 00000000..4cbad673 --- /dev/null +++ b/doc/standard-library/bytes-to-hex.md @@ -0,0 +1,15 @@ + +## BytesToHex + +### Id + +`0x05` +### Signature + +`(bytes: bytes): utf8` + +### Description + +Takes bytes from stack, pushes hex string to the stack diff --git a/doc/standard-library/expand-bytes-evm.md b/doc/standard-library/expand-bytes-evm.md new file mode 100644 index 00000000..039cf484 --- /dev/null +++ b/doc/standard-library/expand-bytes-evm.md @@ -0,0 +1,15 @@ + +## ExpandBytesEvm + +### Id + +`0x08` +### Signature + +`(bytes: bytes): bytes` + +### Description + +Takes bytes from stack. Return expanded to 32 length bytes. diff --git a/doc/ref/vm/stdlib/exponential-function.md b/doc/standard-library/exponential-function.md similarity index 63% rename from doc/ref/vm/stdlib/exponential-function.md rename to doc/standard-library/exponential-function.md index 35e5d163..3e4f5c7c 100644 --- a/doc/ref/vm/stdlib/exponential-function.md +++ b/doc/standard-library/exponential-function.md @@ -8,7 +8,7 @@ THIS FILE IS GENERATED. DO NOT EDIT MANUALLY! `0x03` ### Signature -`(x: int32, y: int32): number` +`(x: int8 | int16 | int32 | int64 | number | bigint, y: int8 | int16 | int32 | number | bigint): int8 | int16 | int32 | int64 | number | bigint` ### Description diff --git a/doc/ref/vm/stdlib/hex-to-bytes.md b/doc/standard-library/hex-to-bytes.md similarity index 100% rename from doc/ref/vm/stdlib/hex-to-bytes.md rename to doc/standard-library/hex-to-bytes.md diff --git a/doc/standard-library/read-evm-word.md b/doc/standard-library/read-evm-word.md new file mode 100644 index 00000000..d321f041 --- /dev/null +++ b/doc/standard-library/read-evm-word.md @@ -0,0 +1,15 @@ + +## ReadEvmWord + +### Id + +`0x06` +### Signature + +`(index: bigint, array: array): bytes` + +### Description + +Takes byte array, index from stack. Returns 32 bytes, truncated if necessary, from given index in the given array. diff --git a/doc/ref/vm/stdlib/ripemd160.md b/doc/standard-library/ripemd160.md similarity index 100% rename from doc/ref/vm/stdlib/ripemd160.md rename to doc/standard-library/ripemd160.md diff --git a/doc/standard-library/sha3.md b/doc/standard-library/sha3.md new file mode 100644 index 00000000..c0de2690 --- /dev/null +++ b/doc/standard-library/sha3.md @@ -0,0 +1,15 @@ + +## Sha3 + +### Id + +`0x09` +### Signature + +`(message: bytes | utf8): bytes` + +### Description + +Calculate Keccak-256 hash for message. diff --git a/doc/standard-library/slice-byte-array.md b/doc/standard-library/slice-byte-array.md new file mode 100644 index 00000000..d84c3128 --- /dev/null +++ b/doc/standard-library/slice-byte-array.md @@ -0,0 +1,15 @@ + +## SliceByteArray + +### Id + +`0x06` +### Signature + +`(size: bigint, index: bigint, array: array): bytes` + +### Description + +Takes byte array, index and size from stack. Returns size bytes from given index in the given array. diff --git a/doc/standard-library/slice-evm-memory.md b/doc/standard-library/slice-evm-memory.md new file mode 100644 index 00000000..53ff44be --- /dev/null +++ b/doc/standard-library/slice-evm-memory.md @@ -0,0 +1,15 @@ + +## SliceEvmMemory + +### Id + +`0x08` +### Signature + +`(size: bigint, ind: bigint, array: array): bytes` + +### Description + +Takes byte array, index, size stack. Returns slice of the array between ind and ind + size. diff --git a/doc/ref/vm/stdlib/validate-ed25519-signature.md b/doc/standard-library/validate-ed25519-signature.md similarity index 100% rename from doc/ref/vm/stdlib/validate-ed25519-signature.md rename to doc/standard-library/validate-ed25519-signature.md diff --git a/doc/standard-library/write-evm-word.md b/doc/standard-library/write-evm-word.md new file mode 100644 index 00000000..7f53a421 --- /dev/null +++ b/doc/standard-library/write-evm-word.md @@ -0,0 +1,15 @@ + +## WriteEvmWord + +### Id + +`0x07` +### Signature + +`(bytes: bytes, index: bigint, array: array): array` + +### Description + +Takes byte array, index from stack, bytes to write. Writes 32 bytes, fill with zeros if necessary, from given index in the given array. Returns reference to array diff --git a/doc/standard-library/write-slice-byte-array.md b/doc/standard-library/write-slice-byte-array.md new file mode 100644 index 00000000..ebba9c35 --- /dev/null +++ b/doc/standard-library/write-slice-byte-array.md @@ -0,0 +1,15 @@ + +## WriteSliceByteArray + +### Id + +`0x07` +### Signature + +`(bytes: bytes, index: bigint, array: array): array` + +### Description + +Takes byte array, index from stack, bytes to write. Writes given bytes from given index in the given array. Returns reference to array diff --git a/doc/using-dotnet/classes-translation.md b/doc/using-dotnet/classes-translation.md new file mode 100644 index 00000000..536faa25 --- /dev/null +++ b/doc/using-dotnet/classes-translation.md @@ -0,0 +1,84 @@ +# C# classes translation + +### Program class + +Each C# file that is going to be translated should follow the listed rules: + +* Class with Program methods must be marked with `[Program]` attribute +* Each compiled .exe file must contain exactly one class with `[Program]` attribute. +* Program class can inherit interfaces _[future plans]_ +* Program class contains only public fields and public or private methods. +* Static fields and methods are not allowed. +* Public methods are translated to Program methods. +* Private methods are translated to inner functions, only difference between them and Program methods is that inner functions are not accessible from outside world and can only be called from Program method. +* Fields (that can be only public) are translated to storage items with `utf8("p_")` keys. +* Only one constructor of Program class is allowed. This constructor mustn't have any arguments. + +### User defined classes + +User can freely define classes without `[Program]` attribute. +Objects of these classes are translated to [data `struct`s](../virtual-machine/data.md) and behave very similar to objects in C#. + +Formally this translation follow the listed rules: + +* Class doesn't have `[Program]` attribute, `[Program]` attribute is used for only one class, which is translated to Program methods. +* Interfaces are not translated, they serve only as compile-time entities. +* **All** (`private`, `protected`, `internal` and `public`) fields are translated to `utf8("") -> ` pairs in `struct`. +* **All** methods are translated to `utf8("_")` -> `ref(#)`. +The `` prefix is needed to support overloading and +`` means offset of function that should be called for the `` method of that class. +Overridden methods will point to different ``. +This technique is similar to [Virtual method table](https://en.wikipedia.org/wiki/Virtual_method_table). +* `static` fields are translated to storage items with `utf8("s__")` keys. +* `static` methods are translated to inner functions with `__` names. +* Constructors are translated to inner functions with `_ctor_` names. + +##### Example: + +Suppose we have the following classes definitions in C#: +```c# +interface Vehicle +{ + void ComeIn(String someone); +} + +class Bicycle : Vehicle +{ + public String Owner = "no one"; + + void ComeIn(String someone) { + Owner = someone; + } +} + +class Car : Vehicle +{ + static private bool isBearBurnedDown = false; + static public bool IsBearBurnedDown() + { + return Car.isBearBurnedDown; + } + + public int NumberOfTires; + + public Car(int tires) + { + NumberOfTires = tires. + } + + void ComeIn(String someone) + { + if (someone == "bear") { + Car.isBearBurnedDown = true; + } + } +} +``` + +- `interface Vehicle` won't be translated to anything +- `Bicycle()` constructor will be translated to `Bicycle_ctor` function that creates `struct(utf8("Owner") -> utf8("no one"), utf8("ComeIn_string") -> ref(#)` +- `Bicycle.ComeIn` will be translated to some function (let's call it `function1`) that changes `utf8("Owner")` field in the given `struct`. +- `Car(int)` constructor will be translated to `Car_ctor_int32` function that creates `struct(utf8("NumberOfTires") -> int32(), utf8("ComeIn_string") -> ref(#))` +- `isBearBurnedDown` static field will be translated to `utf8("s_Car_isBearBurnedDown") -> bool` storage item +- `IsBearBurnedDown` static method will be translated to `Car_IsBearBurnedDown` function that reads `utf8("s_Car_isBearBurnedDown")` storage key. +- `Car.ComeIn` will be translated to some function (let's call it `function2`) that changes `utf8("s_Car_isBearBurnedDown") -> bool` storage item according to the given `String` from the stack. \ No newline at end of file diff --git a/doc/using-dotnet/translation.md b/doc/using-dotnet/translation.md new file mode 100644 index 00000000..2fbe5ef6 --- /dev/null +++ b/doc/using-dotnet/translation.md @@ -0,0 +1,47 @@ +# C# translation overview + +Pravda project allows you to write _programs_ in subset of C\# language. +Pravda Translator translates [CIL](https://en.wikipedia.org/wiki/Common_Intermediate_Language) to Pravda bytecode. + +## Supported C# subset + +Pravda Translator supports only part of all C# features. + +For the moment it supports the following: +- Access to the _storage_ via class fields; +- Access to the _storage_ via `Mapping` (`get`, `getDefault`, `put`, `exists` methods); +- Access to sender address via `Info.Sender()` method; +- Access to list of contract's callers' addresses via `Info.Callers()` method; +- Class methods that are translated to program methods; +- Integer primitive types (`int`, `short`, `byte`, `uint`) and `bool`; +- Basic arithmetics and logical operations; +- Local variables and method arguments; +- If conditions and loops; +- `String`s and auxiliary methods (`+`, access to particular chars, `Slice`); +- `Bytes` (immutable byte arrays), auxiliary methods (access to particular bytes, `Slice`, `Concat`), creation from `byte[]`: `new Bytes(bytes_array)`; +- Arrays of primitive types (`int`, `byte`, `String`), reading and writing of particular elements; +- Explicit conversion of primitive types via +`System.Convert.ToByte`, `System.Convert.ToChar`,`System.Convert.ToInt16`,`System.Convert.ToInt32`,`System.Convert.ToDouble`,`System.Convert.ToBoolean`,`System.Convert.ToString` +- Cryptographic functions: Ripemd160 hashing, validation of Ed25519 Signature. See more in [Standard library](../virtual-machine/stdlib.md) docs. +- User defined classes (although you can't store them in the storage yet). +- Calling other programs via `ProgramHelper.Program<...>` interface. +See some examples ([Pcall.cs](https://github.com/expload/pravda/blob/master/dotnet-tests/resources/Pcall.cs), [PcallProgram.cs](https://github.com/expload/pravda/blob/master/dotnet-tests/resources/PcallProgram.cs)). +- Create events in your program via `Log.Event("name of event", )`, see [Event.cs](https://github.com/expload/pravda/blob/master/dotnet-tests/resources/Event.cs) + +Things that are *not* supported: +- Standard C# library (except of some specific functions from the list above); +- Standard C# collections. + +**Important note**: All code placed in `Expload.Pravda` namespace is ignored. +It is needed to support stub methods in `Pravda.dll`. + +## Examples + +You can look at several examples of test _programs_ to learn current abilities of translation: +- [String examples](https://github.com/expload/pravda/blob/master/dotnet-tests/resources/Strings.cs) that show how to operate with `String`s. +- [Array examples](https://github.com/expload/pravda/blob/master/dotnet-tests/resources/Arrays.cs) that show how to operate with arrays. +- [Simple program](https://github.com/expload/pravda/blob/master/dotnet-tests/resources/SmartProgram.cs) with `balanceOf` and `transfer` methods similar to corresponding methods from [ERC20](https://theethereum.wiki/w/index.php/ERC20_Token_Standard) +- [Buffer](https://github.com/expload/pravda/blob/master/dotnet-tests/resources/IntBuffer.cs) -- Dynamic resizable array implemented in C#. +- [Zoo program](https://github.com/expload/pravda/blob/master/dotnet-tests/resources/ZooProgram.cs) that allows you to create zoos, pets and breed them. +- [Poker program](https://github.com/expload/pravda/blob/master/dotnet-tests/resources/Poker.cs) that implements simple poker game on the blockchain. _(poker.cs was provided by [Ducatur team](https://github.com/DucaturFw/ExploadHackathonContract)) + diff --git a/doc/ref/vm/asm.md b/doc/virtual-machine/asm.md similarity index 75% rename from doc/ref/vm/asm.md rename to doc/virtual-machine/asm.md index e6e4cf43..7e69d855 100644 --- a/doc/ref/vm/asm.md +++ b/doc/virtual-machine/asm.md @@ -1,4 +1,4 @@ -# Pravda Assembler +# Assembler Pravda assembler (pasm) is a text representation of Pravda VM bytecode. @@ -26,9 +26,9 @@ It pops item from the stack and multiplies it by 2. If it is less than 8 it push 1. You can define labels: `@my_label:`. 2. Jump to defined labels `jump @my_label`. Jump with condition `jumi @my_label` and jump to functions with preserved call-stack `call @my_label`. -3. Push primitive to the stack:`push [primitive]`. Or put item to the heap: `new [data]` (in this case reference to data will be pushed to the stack). +3. Push primitive to the stack:`push `. Or put item to the heap: `new ` (in this case reference to data will be pushed to the stack). 4. Write comments: `/* a comment */`. -5. Work with structs: `struct_mut [primitive]`, `struct_get [primitive]`. This will produce `STRUCT_MUT_STATIC` and `STRUCT_GET_STATIC` opcodes which take key for struct field from bytecode. You can write `struct_mut` or `struct_get` without `[primitive]` literal. In this case `STRUCT_MUT` and `STRUCT_GET` opcodes are used and key is taken from stack. +5. Work with structs: `struct_mut `, `struct_get `. This will produce `STRUCT_MUT_STATIC` and `STRUCT_GET_STATIC` opcodes which take key for struct field from bytecode. You can write `struct_mut` or `struct_get` without `` literal. In this case `STRUCT_MUT` and `STRUCT_GET` opcodes are used and key is taken from stack. 6. Use regular orphan [opcodes](opcodes.md). 7. Add meta information via `meta `, see [`` definition](meta.md) for details. diff --git a/doc/ref/vm/data.md b/doc/virtual-machine/data.md similarity index 85% rename from doc/ref/vm/data.md rename to doc/virtual-machine/data.md index 9dca11b4..d1e2c97a 100644 --- a/doc/ref/vm/data.md +++ b/doc/virtual-machine/data.md @@ -1,4 +1,4 @@ -# Pravda internal data format +# Internal data format ## String representation @@ -7,15 +7,14 @@ Human-readable representation of `vm.Data`. Supported by assembler for PravdaVM. ### Primitive types ``` -int8, int16, int32, -uint8, uint16, uint32 -bitint, number +int8, int16, int32, int64 +bigint, number ref, boolean, utf8, bytes ``` -1. All numbers encodes as `type(number)`. For example: `int16(500)` or `number(12.0)`. You can use decimal and hexadecimal way of writing for integers. Also you can write only a number and nearest type will be inferred automatically. For example: `4` will be `uint8`, `-500` will be `int16`. +1. All numbers encodes as `type(number)`. For example: `int16(500)` or `number(12.0)`. You can use decimal and hexadecimal way of writing for integers. Also you can write only a number and nearest type will be inferred automatically. For example: `4` will be `int8`, `-500` will be `int16`. 2. Booleans encodes as `true` and `false`. 3. Refs encodes as `#0x0000`. 4. UTF8 string encodes classically `"hello world"`. @@ -52,9 +51,7 @@ int8 := 0x01 int16 := 0x02 int32 := 0x03 bigint := 0x04 -uint8 := 0x05 -uint16 := 0x06 -uint32 := 0x07 +int64 := 0x05 decimal := 0x08 boolean := 0x09 ref := 0x0A @@ -66,10 +63,8 @@ bytestr := 0x0E primitive_type := int8 | int16 | int32 - | bigint - | uint8 - | uint16 - | uint256 + | int64 + | bigint | double | boolean | ref @@ -84,10 +79,8 @@ type := primitive_type primitive := int8 bytes~1 | int16 bytes~2 | int32 bytes~4 - | bitint length bytes[&length] - | uint8 bytes~1 - | uint16 bytes~2 - | uint32 bytes~4 + | int64 bytes~8 + | bigint length bytes[&length] | double bytes~8 # strict IEEE-754 floating point number | ref byte[4] # ref is constant sized | boolean @@ -111,21 +104,19 @@ data := primitive ### Primitives -All primitives encodes as JSON strings with prefix. It's easy to parse. Most of popular languages have `indexOf` and `substring` functions. Type always before first dot, value after. +All primitives encodes as JSON strings with prefix. It's easy to parse. Most of popular languages have `indexOf` and `substring` functions. Type always situated before first dot, value after. ```json "int8.-100" "int16.-100" "int32.-100" -"uint8.100" -"uint16.100" -"uint32.1000" -"bitint.9999999999999" +"int64.-100" +"bigint.9999999999999" "number.2.0" "ref.1" "bool.true" "utf8.i am cow" -"bytes.01fca4e91" +"bytes.01fca4e9" "null" ``` @@ -146,4 +137,4 @@ Structs corresponds to JSON objects. "utf8.user": "ref.9153", "int32.1432": "bytes.41f8cff6" } -``` \ No newline at end of file +``` diff --git a/doc/virtual-machine/meta.md b/doc/virtual-machine/meta.md new file mode 100644 index 00000000..434c6bfb --- /dev/null +++ b/doc/virtual-machine/meta.md @@ -0,0 +1,13 @@ +# Meta in bytecode + +Pravda bytecode may contain additional information for disassembler, code generator and other tools that read and interpreter Pravda bytecode. +This information is called `meta`. + +There're 5 kinds of `meta` for the moment: +- `label_def ""` that marks definition of a label; +- `label_use ""` that marks usage of a label; +- `program_name ""` that contains a name of a _program_; +- `method ` (see `struct` definition in [data spec](data.md)). +- `source_mark ` that specifies information about source files. +- `translator_mark ""` that contains arbitrary information. \ No newline at end of file diff --git a/doc/ref/vm/opcodes.md b/doc/virtual-machine/opcodes.md similarity index 74% rename from doc/ref/vm/opcodes.md rename to doc/virtual-machine/opcodes.md index 915d910d..4a96d1f0 100644 --- a/doc/ref/vm/opcodes.md +++ b/doc/virtual-machine/opcodes.md @@ -2,58 +2,61 @@ THIS FILE IS GENERATED. DO NOT EDIT MANUALLY! --> # Pravda VM opcodes -Code|Mnemonic |Description -----|-------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -0x00|stop |Stops program execution. -0x01|jump |Alters program execution counter to value of first item of the stack. -0x02|jumpi |If boolean value in head of stack is true then alters program execution counter to value of second item in the stack. -0x04|call |Firstly, it pushes current program counter to the separate stack (so called 'call stack'). Then it alters program execution counter to the value of the first item of the stack. -0x05|ret |Alters program execution counter to the value of the first item of the call stack (see CALL opcode). -0x06|pcall |Takes two words by which it is followed. They are address `a` and the number of parameters `n`, respectively. Then it executes the program with the address `a` and passes there only $n$ top elements of the stack. -0x07|lcall |Takes three words by which it is followed.They are address `a`, function `f` and the number of parameters `n`, respectively. Then it executes the function `f` of the library (which is a special form of program) with the address `a` and passes there only $n$ top elements of the stack. -0x08|scall |Takes id of function from standard library and execute it. -0x10|pop |Removes first item from the stack. -0x11|push |Pushes the data primitive following the opcode to the stack. Refs are prohibited -0x12|dup |Duplicates first item of the stack. -0x13|dupn |Duplicates `(n+1)`-th item of the stack where `n` is the first item in stack. -0x14|swap |Swaps first two items in the stack. -0x15|swapn |Swaps the second item of the stack with the `(n+1)`-th item of the stack where `n` is first item in the stack. -0x20|new |Puts the data following the opcode to the heap. Pushes reference to the stack. Refs in structs and ref arrays are prohibited. -0x21|array_get |Takes reference to array and index from the stack.Pushes to the stack a primitive at index in array corresponding by the given reference. -0x22|struct_get |Takes reference to struct and key from the stack.Pushes to the stack a primitive at key in struct corresponding by the given reference. -0x24|array_mut |Takes reference to array, primitive and index from the stack.Puts a primitive at index in array corresponding by the given reference. -0x25|struct_mut |Takes key, primitive and reference to struct from the stack.Puts a primitive at key in struct corresponding by the given reference. -0x27|primitive_put|Puts top item from the stack to the memory.Pushes reference to the stack. -0x28|primitive_get|Uses top item from the stack as referenceto data in the memory of program. Pushesretrieved data to the stack. -0x29|new_array |Takes type of desired array from the stack. Takes length of the desired array from the stack. Pushes reference of new array to the stack. -0x30|length |Takes reference to array or Bytes or Utf8 from stack. Pushes length of given array, Bytes or Utf8 to the stack. -0x50|sput |Pops first item from stack, interprets it as key. Pops second item from stack, interprets it as value. Puts (key -> value) record to program's storage. -0x51|sget |Pops first item from stack, interprets it as key, retrieves corresponding record from a storage of the program and pushes the record to the stack. Otherwise throws an exception. -0x52|sdrop |Pops first item from stack, interprets it as key and removes corresponding record from a storage of the program. -0x53|sexist |Pops first item from stack, interprets it as key and checks existence of record correspond to the key in a storage of the program. -0x60|add |Makes '+' operation on two top items from stack. Pushes result to stack. -0x61|mul |Makes '*' operation on two top items from stack. Pushes result to stack. -0x62|div |Makes '/' operation on two top items from stack. Pushes result to stack. -0x63|mod |Makes '%' operation on two top items from stack. Pushes result to stack. -0x67|lt |Checks top stack item is less than subsequent stack item. Pushes Bool result to stack. -0x68|gt |Checks top stack item is greater than subsequent stack item.Pushes Bool result to stack. -0x80|not |Logical NOT (negation).Pops items from stack.If it's 'true' pushes 'false' to stack.Its it's 'false' pushes 'true' to stack. -0x81|and |Makes 'and' operation on two items from stack. Pushes result to stack. -0x82|or |Makes 'or' operation on two items from stack. Pushes result to stack. -0x83|xor |Makes 'xor' operation on two items from stack.Pushes result to stack. -0x84|eq |Checks top stack item is equal to subsequent stack item. Pushes Bool result to stack. -0x90|cast |Casts primitive to another type. -0x91|concat |Takes two items from stack. Concatenates them and put result to stack. -0x92|slice |Takes start index, end index and item from the stack. Makes slice of item and puts result to the stack. -0xA0|from |Gives current executor address. -0xA1|meta | -0xA2|paddr |Gives current program address. -0xA5|pcreate |Takes address (pubKey), bytecode, and its ed25519 signature. If signature is valid and program didn't exist before on the specified address create new program. -0xA6|pupdate |Takes address of an existing program, code and signature. Signature is computed from concatenation of old code and new code of the program -0xA7|code |Take address of a program. Pushes program bytecode to the stack -0xA8|seal |Takes the address of an existing program and signature of code with seal mark. Signature is computed from concatenation of 'Seal' word (in UTF8 encoding) and current code of program. -0xA9|throw |Takes string from stack and throws an error with description as given string that stops the program. -0xAA|event |Takes string and arbitrary data from stack, create new event with name as given string and with given data. -0xC0|transfer |Gets two parameters `a` and `n` from the stack and transfers `n` native coins from the executor account to the account `a`. -0xC1|ptransfer |Gets two parameters `a` and `n` from the stack and transfers `n` native coins from the current program account to the account `a` -0xC2|balance |Takes address from stack, pushes native coin balance to the stack \ No newline at end of file +Code|Mnemonic |Description +----|-------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +0x00|stop |Stops program execution. +0x01|jump |Alters program execution counter to value of first item of the stack. +0x02|jumpi |If boolean value in head of stack is true then alters program execution counter to value of second item in the stack. +0x04|call |Firstly, it pushes current program counter to the separate stack (so called 'call stack'). Then it alters program execution counter to the value of the first item of the stack. +0x05|ret |Alters program execution counter to the value of the first item of the call stack (see CALL opcode). +0x06|pcall |Takes two words by which it is followed. They are address `a` and the number of parameters `n`, respectively. Then it executes the program with the address `a` and passes there only $n$ top elements of the stack. +0x07|lcall |Takes three words by which it is followed.They are address `a`, function `f` and the number of parameters `n`, respectively. Then it executes the function `f` of the library (which is a special form of program) with the address `a` and passes there only $n$ top elements of the stack. +0x08|scall |Takes id of function from standard library and execute it. +0x10|pop |Removes first item from the stack. +0x11|push |Pushes the data primitive following the opcode to the stack. Refs are prohibited +0x12|dup |Duplicates first item of the stack. +0x13|dupn |Duplicates `(n+1)`-th item of the stack where `n` is the first item in stack. +0x14|swap |Swaps first two items in the stack. +0x15|swapn |Swaps the second item of the stack with the `(n+1)`-th item of the stack where `n` is first item in the stack. +0x20|new |Puts the data following the opcode to the heap. Pushes reference to the stack. Refs in structs and ref arrays are prohibited. +0x21|array_get |Takes reference to array and index from the stack.Pushes to the stack a primitive at index in array corresponding by the given reference. +0x22|struct_get |Takes reference to struct and key from the stack.Pushes to the stack a primitive at key in struct corresponding by the given reference. +0x24|array_mut |Takes reference to array, primitive and index from the stack.Puts a primitive at index in array corresponding by the given reference. +0x25|struct_mut |Takes key, primitive and reference to struct from the stack.Puts a primitive at key in struct corresponding by the given reference. +0x27|primitive_put|Puts top item from the stack to the memory.Pushes reference to the stack. +0x28|primitive_get|Uses top item from the stack as referenceto data in the memory of program. Pushesretrieved data to the stack. +0x29|new_array |Takes type of desired array from the stack. Takes length of the desired array from the stack. Pushes reference of new array to the stack. +0x30|length |Takes reference to array or Bytes or Utf8 from stack. Pushes length of given array, Bytes or Utf8 to the stack. +0x50|sput |Pops first item from stack, interprets it as key. Pops second item from stack, interprets it as value. Puts (key -> value) record to program's storage. If value is a ref, correspondent value will be taken from heap. Referenced value shouldn't be RefArray and shouldn't be Struct with refs in field values. +0x51|sget |Pops first item from stack, interprets it as key, retrieves corresponding record from a storage of the program and pushes the record to the stack. Otherwise throws an exception. +0x52|sdrop |Pops first item from stack, interprets it as key and removes corresponding record from a storage of the program. +0x53|sexist |Pops first item from stack, interprets it as key and checks existence of record correspond to the key in a storage of the program. +0x60|add |Makes '+' operation on two top items from stack. Pushes result to stack. +0x61|mul |Makes '*' operation on two top items from stack. Pushes result to stack. +0x62|div |Makes '/' operation on two top items from stack. Pushes result to stack. +0x63|mod |Makes '%' operation on two top items from stack. Pushes result to stack. +0x67|lt |Checks top stack item is less than subsequent stack item. Pushes Bool result to stack. +0x68|gt |Checks top stack item is greater than subsequent stack item.Pushes Bool result to stack. +0x80|not |Logical NOT (negation).Pops items from stack.If it's 'true' pushes 'false' to stack.Its it's 'false' pushes 'true' to stack. +0x81|and |Makes 'and' operation on two items from stack. Pushes result to stack. +0x82|or |Makes 'or' operation on two items from stack. Pushes result to stack. +0x83|xor |Makes 'xor' operation on two items from stack.Pushes result to stack. +0x84|eq |Checks top stack item is equal to subsequent stack item. Pushes Bool result to stack. +0x90|cast |Casts primitive to another type. +0x91|concat |Takes two items from stack. Concatenates them and put result to stack. +0x92|slice |Takes start index, end index and item from the stack. Makes slice of item and puts result to the stack. +0xA0|from |Gives current executor address. +0xA1|meta | +0xA2|paddr |Gives current program address. +0xA5|pcreate |Takes address (pubKey), bytecode, and its ed25519 signature. If signature is valid and program didn't exist before on the specified address create new program. +0xA6|pupdate |Takes address of an existing program, code and signature. Signature is computed from concatenation of old code and new code of the program +0xA7|code |Take address of a program. Pushes program bytecode to the stack +0xA8|seal |Takes the address of an existing program and signature of code with seal mark. Signature is computed from concatenation of 'Seal' word (in UTF8 encoding) and current code of program. +0xA9|throw |Takes string from stack and throws an error with description as given string that stops the program. +0xAA|event |Takes string and arbitrary data from stack, create new event with name as given string and with given data. +0xAB|callers |Gets caller's 'call stack' (see CALL opcode) and pushes it to the stack +0xAC|height |Gets current height of the blockchain and pushes it to the stack. +0xAD|hash |Gets hash of the last block and pushes it to the stack. +0xC0|transfer |Gets two parameters `a` and `n` from the stack and transfers `n` native coins from the executor account to the account `a`. +0xC1|ptransfer |Gets two parameters `a` and `n` from the stack and transfers `n` native coins from the current program account to the account `a` +0xC2|balance |Takes address from stack, pushes native coin balance to the stack \ No newline at end of file diff --git a/docker/images/broadcaster/Dockerfile b/docker/images/broadcaster/Dockerfile new file mode 100644 index 00000000..ad08da64 --- /dev/null +++ b/docker/images/broadcaster/Dockerfile @@ -0,0 +1,12 @@ +FROM openjdk:8u171 + +WORKDIR /broadcaster + +COPY ./services/broadcaster/target/universal/stage/lib lib +COPY ./services/broadcaster/target/universal/stage/bin bin + +COPY docker/images/broadcaster/entry.sh /broadcaster + +EXPOSE 5000 + +ENTRYPOINT [ "/broadcaster/entry.sh" ] diff --git a/docker/images/broadcaster/entry.sh b/docker/images/broadcaster/entry.sh new file mode 100755 index 00000000..f30dbd06 --- /dev/null +++ b/docker/images/broadcaster/entry.sh @@ -0,0 +1,3 @@ +#!/bin/sh +echo "Starting Pravda Broadcaster" +bin/pravda-broadcaster diff --git a/docker/images/expload-pravda-node/Dockerfile b/docker/images/expload-pravda-node/Dockerfile deleted file mode 100644 index 5fa45cd8..00000000 --- a/docker/images/expload-pravda-node/Dockerfile +++ /dev/null @@ -1,32 +0,0 @@ -from gcr.io/time-coin/sbt:latest as builder - -workdir /build - -copy cli cli -copy codegen codegen -copy common common -copy dotnet dotnet -copy node-db node-db -copy node node -copy vm-api vm-api -copy vm-asm vm-asm -copy vm vm -copy yopt yopt - -copy project project -copy build.sbt build.sbt - -run cd /build && SBT_OPTS="-Xmx2G -Xms2G -XX:+UseConcMarkSweepGC -XX:+CMSClassUnloadingEnabled -Xss2M" sbt cli/stage - -from openjdk:8u171 - -workdir /pravda-cli - -copy --from=builder build/cli/target/universal/stage/lib lib -copy --from=builder build/cli/target/universal/stage/bin bin - -copy docker/images/expload-pravda-node/coin-distr.json /pravda-cli -copy docker/images/expload-pravda-node/application.conf /pravda-cli -copy docker/images/expload-pravda-node/entry.sh /pravda-cli - -entrypoint [ "/pravda-cli/entry.sh" ] diff --git a/docker/images/expload-pravda-node/application.conf b/docker/images/expload-pravda-node/application.conf deleted file mode 100644 index 9ad380f8..00000000 --- a/docker/images/expload-pravda-node/application.conf +++ /dev/null @@ -1,36 +0,0 @@ -pravda { - http { - host = "0.0.0.0" - host = ${?PRAVDA_HTTP_HOST} - port = 8080 - port = ${?PRAVDA_HTTP_PORT} - } - tendermint { - peer-port = 46656 - peer-port = ${?PRAVDA_P2P_PORT} - rpc-port = 46657 - rpc-port = ${?PRAVDA_RPC_PORT} - proxy-app-port = 46658 - proxy-app-port = ${?PRAVDA_ABCI_PORT} - use-unix-domain-socket = false - use-unix-domain-socket = ${?PRAVDA_USE_UNIX_SOCKET} - } - is-validator = ${?PRAVDA_IS_VALIDATOR} - data-directory = ${?PRAVDA_DATA} - seeds = ${?PRAVDA_SEEDS} - coin-distribution = ${?PRAVDA_COIN_DISTRIBUTION} - genesis { - # 0001-01-01T00:00:00Z - time = ${?PRAVDA_GENESIS_TIME} - chain-id = ${?PRAVDA_GENESIS_CHAIN_ID} - # "name:power:key,..." - validators = ${?PRAVDA_GENESIS_VALIDATORS} - app-hash = "" # Always empty by now - distribution = false - distribution = ${?PRAVDA_DISTRIBUTION} - } - payment-wallet { - private-key = ${?PRAVDA_VALIDATOR_SK} - address = ${?PRAVDA_VALIDATOR_PK} - } -} diff --git a/docker/images/pravda-cli/Dockerfile b/docker/images/pravda-cli/Dockerfile new file mode 100644 index 00000000..5c708af2 --- /dev/null +++ b/docker/images/pravda-cli/Dockerfile @@ -0,0 +1,11 @@ +FROM openjdk:8u171 + +WORKDIR /pravda-cli + +COPY ./cli/target/universal/stage/lib lib +COPY ./cli/target/universal/stage/bin bin + +COPY docker/images/pravda-cli/coin-distr.json /pravda-cli +COPY docker/images/pravda-cli/entry.sh /pravda-cli + +ENTRYPOINT [ "/pravda-cli/entry.sh" ] diff --git a/docker/images/expload-pravda-node/README.md b/docker/images/pravda-cli/README.md similarity index 100% rename from docker/images/expload-pravda-node/README.md rename to docker/images/pravda-cli/README.md diff --git a/docker/images/expload-pravda-node/coin-distr.json b/docker/images/pravda-cli/coin-distr.json similarity index 100% rename from docker/images/expload-pravda-node/coin-distr.json rename to docker/images/pravda-cli/coin-distr.json diff --git a/docker/images/expload-pravda-node/entry.sh b/docker/images/pravda-cli/entry.sh similarity index 55% rename from docker/images/expload-pravda-node/entry.sh rename to docker/images/pravda-cli/entry.sh index 32941949..92e67eec 100755 --- a/docker/images/expload-pravda-node/entry.sh +++ b/docker/images/pravda-cli/entry.sh @@ -1,7 +1,5 @@ #!/bin/sh -set -e - if [ -z "$PRAVDA_COIN_HOLDER" ] then echo "Please specify public key in \$PRAVDA_COIN_HOLDER to perform initial coin distribution" @@ -12,18 +10,17 @@ fi sed -i "s/PRAVDA_COIN_HOLDER/$PRAVDA_COIN_HOLDER/g" /pravda-cli/coin-distr.json -if [ -z "$(ls -A /node-data)" ]; then - echo "/node-data does not exist, creating" - mkdir -p /node-data +if [ ! -d ${PRAVDA_DATA} ]; then + echo "${PRAVDA_DATA} does not exist, creating" + mkdir -p ${PRAVDA_DATA} fi if [ ! -f /node-data/initialized ]; then echo "Initializing node" - rm -rf /node-data/* - /pravda-cli/bin/pravda node init --data-dir /node-data --coin-distribution coin-distr.json + rm -rf ${PRAVDA_DATA}/* + bin/pravda node init --data-dir ${PRAVDA_DATA} --coin-distribution coin-distr.json echo yes > /node-data/initialized fi echo "Starting node" -export TC_CONFIG_FILE=/pravda-cli/application.conf -/pravda-cli/bin/pravda node run --data-dir /node-data +bin/pravda node run --data-dir ${PRAVDA_DATA} diff --git a/docker/images/pravda-faucet/Dockerfile b/docker/images/pravda-faucet/Dockerfile new file mode 100644 index 00000000..74f227e6 --- /dev/null +++ b/docker/images/pravda-faucet/Dockerfile @@ -0,0 +1,10 @@ +FROM openjdk:8u171 + +WORKDIR /faucet + +COPY ./faucet/target/universal/stage/lib lib +COPY ./faucet/target/universal/stage/bin bin + +COPY docker/images/pravda-faucet/entry.sh /faucet + +ENTRYPOINT [ "/faucet/entry.sh" ] diff --git a/docker/images/pravda-faucet/entry.sh b/docker/images/pravda-faucet/entry.sh new file mode 100755 index 00000000..18b02d0d --- /dev/null +++ b/docker/images/pravda-faucet/entry.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +echo "Starting faucet service" +bin/pravda-faucet diff --git a/dotnet-tests/resources/Arithmetics.cs b/dotnet-tests/resources/Arithmetics.cs new file mode 100644 index 00000000..609a12e8 --- /dev/null +++ b/dotnet-tests/resources/Arithmetics.cs @@ -0,0 +1,18 @@ +using System; +using Expload.Pravda; + +[Program] +public class Arithmetics { + private int X = 10; + + public int TestBasicOperations() + { + int a = X + 2; + int b = X * 2; + int c = X / 2; + int d = X % 2; + return ((a + b + 42) * c + d) / 1337; + } + + public static void Main() {} +} \ No newline at end of file diff --git a/dotnet-tests/resources/arrays.cs b/dotnet-tests/resources/Arrays.cs similarity index 54% rename from dotnet-tests/resources/arrays.cs rename to dotnet-tests/resources/Arrays.cs index da2c2ed3..c4e893c7 100644 --- a/dotnet-tests/resources/arrays.cs +++ b/dotnet-tests/resources/Arrays.cs @@ -2,25 +2,25 @@ using Expload.Pravda; [Program] -public class ProgramArrays +public class Arrays { - public Mapping bytes = new Mapping(); + private Mapping BytesMapping = new Mapping(); - public void WorkWithBytes() { - byte[] arr1 = new byte[] {1, 2, 3}; + public void TestByteArrays() { + sbyte[] arr1 = new sbyte[] {1, 2, 3}; Bytes bytes1 = new Bytes(4, 5, 6); Bytes bytes2 = new Bytes(7, 8, 9); - byte b1 = arr1[0]; - byte b2 = arr1[2]; - byte b3 = bytes1[1]; - byte b4 = bytes2[1]; + sbyte b1 = arr1[0]; + sbyte b2 = arr1[2]; + sbyte b3 = bytes1[1]; + sbyte b4 = bytes2[1]; Bytes bytes3 = bytes1.Slice(1, 2); - bytes.put(bytes1, bytes2); - if (bytes.exists(new Bytes(8, 9, 10))) { - bytes.put(bytes1, new Bytes(7, 8, 9)); + BytesMapping[bytes1] = bytes2; + if (BytesMapping.ContainsKey(new Bytes(8, 9, 10))) { + BytesMapping[bytes1] = new Bytes(7, 8, 9); } arr1[0] = 2; @@ -29,18 +29,18 @@ public void WorkWithBytes() { int len = bytes1.Length(); } - public void WorkWithArrays() { + public void TestAllArrays() { char[] chars = new char[] { 'a', 'b', 'c' }; int[] ints = new int[] { 1, 2, 3 }; double[] doubles = new double[] { 1.0, 2.0, 3.0 }; string[] strings = new string[] { "abc", "def", "rty" }; - uint[] uints = new uint[] { 4, 5, 6 }; + long[] longs = new long[] { 4L, 5L, 6L }; chars[1] = 'd'; ints[1] = 4; doubles[1] = 4.0; strings[1] = "asdf"; - uints[1] = 7; + longs[1] = 7L; int len = strings.Length; } diff --git a/dotnet-tests/resources/Block.cs b/dotnet-tests/resources/Block.cs new file mode 100644 index 00000000..13515432 --- /dev/null +++ b/dotnet-tests/resources/Block.cs @@ -0,0 +1,18 @@ +using System; +using Expload.Pravda; + +[Program] +public class Block +{ + public long TestHeightMethod() + { + return Info.Height(); + } + + public Bytes TestLastBlockHash() + { + return Info.LastBlockHash(); + } + + public static void Main() {} +} \ No newline at end of file diff --git a/dotnet-tests/resources/Callers.cs b/dotnet-tests/resources/Callers.cs new file mode 100644 index 00000000..b0dba8c7 --- /dev/null +++ b/dotnet-tests/resources/Callers.cs @@ -0,0 +1,16 @@ +using System; +using Expload.Pravda; + +namespace PcallNamespace { + + [Program] + public class Callers + { + public Bytes TestCallers() + { + return Info.Callers()[0]; + } + + public static void Main() {} + } +} \ No newline at end of file diff --git a/dotnet-tests/resources/Closure.cs b/dotnet-tests/resources/Closure.cs new file mode 100644 index 00000000..7611e2d8 --- /dev/null +++ b/dotnet-tests/resources/Closure.cs @@ -0,0 +1,15 @@ +using System; +using Expload.Pravda; + +[Program] +public class Closure +{ + public int TestClosure() + { + int e = 1; + Func d = x => x + e; + return d(3); + } + + public static void Main() {} +} \ No newline at end of file diff --git a/dotnet-tests/resources/Compare.cs b/dotnet-tests/resources/Compare.cs new file mode 100644 index 00000000..8f2e30da --- /dev/null +++ b/dotnet-tests/resources/Compare.cs @@ -0,0 +1,92 @@ +using Expload.Pravda; + +[Program] +public class Compare +{ + public bool TestCompare() + { + int a = 1; + int b = 2; + short c = 3; + short d = 4; + long e = 5L; + long f = 6L; + + bool tmp = true; + tmp &= a == b; + tmp &= a == c; + tmp &= c == d; + tmp &= c == e; + tmp &= e == f; + + if (a == b) { tmp = true; } + if (a == c) { tmp = true; } + if (c == d) { tmp = true; } + if (c == e) { tmp = true; } + if (e == f) { tmp = true; } + + tmp &= a >= b; + tmp &= a >= c; + tmp &= c >= d; + tmp &= c >= e; + tmp &= e >= f; + + if (a >= b) { tmp = true; } + if (a >= c) { tmp = true; } + if (c >= d) { tmp = true; } + if (c >= e) { tmp = true; } + if (e >= f) { tmp = true; } + + tmp &= a <= b; + tmp &= a <= c; + tmp &= c <= d; + tmp &= c <= e; + tmp &= e <= f; + + if (a <= b) { tmp = true; } + if (a <= c) { tmp = true; } + if (c <= d) { tmp = true; } + if (c <= e) { tmp = true; } + if (e <= f) { tmp = true; } + + tmp &= a != b; + tmp &= a != c; + tmp &= c != d; + tmp &= c != e; + tmp &= e != f; + + if (a != b) { tmp = true; } + if (a != c) { tmp = true; } + if (c != d) { tmp = true; } + if (c != e) { tmp = true; } + if (e != f) { tmp = true; } + + tmp &= a > b; + tmp &= a > c; + tmp &= c > d; + tmp &= c > e; + tmp &= e > f; + + if (a > b) { tmp = true; } + if (a > c) { tmp = true; } + if (c > d) { tmp = true; } + if (c > e) { tmp = true; } + if (e > f) { tmp = true; } + + tmp &= a < b; + tmp &= a < c; + tmp &= c < d; + tmp &= c < e; + tmp &= e < f; + + if (a < b) { tmp = true; } + if (a < c) { tmp = true; } + if (c < d) { tmp = true; } + if (c < e) { tmp = true; } + if (e < f) { tmp = true; } + + return tmp; + } + + public static void Main() {} +} \ No newline at end of file diff --git a/dotnet-tests/resources/ConcatStrings.cs b/dotnet-tests/resources/ConcatStrings.cs new file mode 100644 index 00000000..207d8865 --- /dev/null +++ b/dotnet-tests/resources/ConcatStrings.cs @@ -0,0 +1,24 @@ +using System; +using Expload.Pravda; + +[Program] +public class ConcatStrings +{ + public string TestConcatStrings() + { + string s = "s"; + string c2 = s + s + "2"; + string c3 = s + s + s + "3"; + string c4 = s + s + s + s + "4"; + string c5 = s + s + s + s + s + "5"; + string c6 = s + s + s + s + s + s + "6"; + string c7 = s + s + s + s + s + s + s + "7"; + string c8 = s + s + s + s + s + s + s + s + "8"; + string c9 = s + s + s + s + s + s + s + s + s + "9"; + string c10 = s + s + s + s + s + s + s + s + s + s + "10"; + + return c2 + c3 + c4 + c5 + c6 + c7 + c8 + c9 + c10; + } + + static public void Main () {} +} \ No newline at end of file diff --git a/dotnet-tests/resources/error.cs b/dotnet-tests/resources/Error.cs similarity index 75% rename from dotnet-tests/resources/error.cs rename to dotnet-tests/resources/Error.cs index 536734ff..b41a1a51 100644 --- a/dotnet-tests/resources/error.cs +++ b/dotnet-tests/resources/Error.cs @@ -2,9 +2,10 @@ using Expload.Pravda; [Program] -public class ErrorClass +public class Error { - public void ErrorFunc() { + public void TestError() + { Console.WriteLine("Oops! There's no console in the blockchain!"); } diff --git a/dotnet-tests/resources/event.cs b/dotnet-tests/resources/Event.cs similarity index 84% rename from dotnet-tests/resources/event.cs rename to dotnet-tests/resources/Event.cs index ecd3d4b4..6e064926 100644 --- a/dotnet-tests/resources/event.cs +++ b/dotnet-tests/resources/Event.cs @@ -2,8 +2,9 @@ using Expload.Pravda; [Program] -class MyProgram { - public void MakeEvent() { +class Event +{ + public void TestEvent() { Log.Event("my_event", 1234); Log.Event("my_event", "my_string"); Log.Event("my_event", 2.0); diff --git a/dotnet-tests/resources/ExternalMethods.cs b/dotnet-tests/resources/ExternalMethods.cs new file mode 100644 index 00000000..1e9df6b2 --- /dev/null +++ b/dotnet-tests/resources/ExternalMethods.cs @@ -0,0 +1,45 @@ +using System; +using Expload.Pravda; + +namespace ExternalNamespace { + + public class ExternalMethods { + public int A; + public int B; + + public static int Add(int a, int b) { + return a + b; + } + + public ExternalMethods(int a, int b) { + this.A = a; + this.B = b; + } + + public int Add() { + return A + B; + } + + public int Add(int c) { + return A + B + c; + } + } + + [Program] + public class ExternalProgramMethods + { + public static ExternalProgramMethods GetInstance() + { + return ProgramHelper.Program(new Bytes( + "123456789012345678901234567890123456789012345678901234567890ABCD" + )); + } + + public int Add(int a, int b) + { + return a + b; + } + + public static void Main() {} + } +} \ No newline at end of file diff --git a/dotnet-tests/resources/ExternalMethodsCheck.cs b/dotnet-tests/resources/ExternalMethodsCheck.cs new file mode 100644 index 00000000..9e0e5d3d --- /dev/null +++ b/dotnet-tests/resources/ExternalMethodsCheck.cs @@ -0,0 +1,31 @@ +using System; +using Expload.Pravda; + +namespace ExternalNamespace { + + [Program] + public class ExternalMethodsCheck + { + public int TestStaticMethods() + { + ExternalProgramMethods epm = ExternalProgramMethods.GetInstance(); + int a = epm.Add(2, 2); + int b = epm.Add(10, 10); + int c = epm.Add(300, 300); + + return a + b + c; + } + + public int TestRegularMethods() + { + ExternalMethods em = new ExternalMethods(3, 3); + int a = em.Add(); + int b = em.Add(100); + int c = ExternalMethods.Add(1000, 1000); + + return a + b + c; + } + + public static void Main() {} + } +} \ No newline at end of file diff --git a/dotnet-tests/resources/if.cs b/dotnet-tests/resources/If.cs similarity index 92% rename from dotnet-tests/resources/if.cs rename to dotnet-tests/resources/If.cs index 03842aac..23832b83 100644 --- a/dotnet-tests/resources/if.cs +++ b/dotnet-tests/resources/If.cs @@ -1,8 +1,9 @@ using Expload.Pravda; [Program] -public class ProgramIfs { - public void ifs() +public class If +{ + public void TestIfs() { int x = 10; diff --git a/dotnet-tests/resources/inheritance.cs b/dotnet-tests/resources/Inheritance.cs similarity index 90% rename from dotnet-tests/resources/inheritance.cs rename to dotnet-tests/resources/Inheritance.cs index 6894b213..aa68cec4 100644 --- a/dotnet-tests/resources/inheritance.cs +++ b/dotnet-tests/resources/Inheritance.cs @@ -2,10 +2,7 @@ public class Parent { - public Parent(int val) - { - - } + public Parent(int val) {} public virtual int AnswerPlus1() { @@ -49,9 +46,9 @@ public override int Answer() } [Program] -public class MyProgram +public class Inheritance { - public int Func() + public int TestInheritance() { Parent a = new A(100); Parent b = new B(200); diff --git a/dotnet-tests/resources/buffer.cs b/dotnet-tests/resources/IntBuffer.cs similarity index 93% rename from dotnet-tests/resources/buffer.cs rename to dotnet-tests/resources/IntBuffer.cs index c6ea8432..dd201bb9 100644 --- a/dotnet-tests/resources/buffer.cs +++ b/dotnet-tests/resources/IntBuffer.cs @@ -1,7 +1,8 @@ using System; using Expload.Pravda; -public class IntBuffer { +public class IntBuffer +{ private int size = 0; private int[] buffer; @@ -34,9 +35,9 @@ public void Append(int elem) } [Program] -public class MyProgram +public class IntBufferProgram { - public string Func() + public string TestBuffer() { IntBuffer buff = new IntBuffer(2); buff.Append(1); diff --git a/dotnet-tests/resources/LogicOperations.cs b/dotnet-tests/resources/LogicOperations.cs new file mode 100644 index 00000000..3e2c3b99 --- /dev/null +++ b/dotnet-tests/resources/LogicOperations.cs @@ -0,0 +1,21 @@ +using System; +using Expload.Pravda; + +[Program] +public class LogicOperations +{ + private Mapping Log; + + public void TestLogicOperations() + { + Log[1] = Convert.ToString(false || true); + Log[2] = Convert.ToString(false && true); + Log[3] = Convert.ToString(true ^ true); + + Log[4] = Convert.ToString(3 | 5); + Log[5] = Convert.ToString(3 & 5); + Log[6] = Convert.ToString(3 ^ 5); + } + + public static void Main() {} +} \ No newline at end of file diff --git a/dotnet-tests/resources/loop.cs b/dotnet-tests/resources/Loop.cs similarity index 77% rename from dotnet-tests/resources/loop.cs rename to dotnet-tests/resources/Loop.cs index 7a8cf6d2..d0f93a65 100644 --- a/dotnet-tests/resources/loop.cs +++ b/dotnet-tests/resources/Loop.cs @@ -1,9 +1,9 @@ using Expload.Pravda; [Program] -public class ProgramLoops { - - public void loops() +public class Loop +{ + public int TestLoop() { int a = 0; for (int i = 0; i < 10; i++) { @@ -13,6 +13,8 @@ public void loops() while (a < 10000) { a *= 2; } + + return a; } public static void Main() {} diff --git a/dotnet-tests/resources/loop_nested.cs b/dotnet-tests/resources/LoopNested.cs similarity index 82% rename from dotnet-tests/resources/loop_nested.cs rename to dotnet-tests/resources/LoopNested.cs index 54b97931..d8c1be1c 100644 --- a/dotnet-tests/resources/loop_nested.cs +++ b/dotnet-tests/resources/LoopNested.cs @@ -1,9 +1,9 @@ using Expload.Pravda; [Program] -public class ProgramLoops { - - public void loops() +public class LoosNested +{ + public int TestNestedLoop() { int a = 0; for (int i = 0; i < 10; i++) { @@ -17,6 +17,8 @@ public void loops() while (a < 10000) { a *= 2; } + + return a; } public static void Main() {} diff --git a/dotnet-tests/resources/objects.cs b/dotnet-tests/resources/Object.cs similarity index 91% rename from dotnet-tests/resources/objects.cs rename to dotnet-tests/resources/Object.cs index e76b6e82..9e19c3b3 100644 --- a/dotnet-tests/resources/objects.cs +++ b/dotnet-tests/resources/Object.cs @@ -32,9 +32,9 @@ public int AnswerB() } [Program] -public class MyProgram +public class Object { - public int Func() + public int TestObjects() { var a = new A(100); var b = new B(200); diff --git a/dotnet-tests/resources/ObjectGetSet.cs b/dotnet-tests/resources/ObjectGetSet.cs new file mode 100644 index 00000000..86883c28 --- /dev/null +++ b/dotnet-tests/resources/ObjectGetSet.cs @@ -0,0 +1,35 @@ +using System; +using Expload.Pravda; + +public class SomeClass +{ + public int field1; + public int field2 { get; set; } + private int field3; + + public void SetField3(int field3) + { + this.field3 = field3; + } + + public int GetField3() + { + return field3; + } +} + +[Program] +public class ObjectGetSet +{ + public int TestObjectGetSet() + { + var cls = new SomeClass(); + cls.field1 = 3; + cls.field2 = 20; + cls.SetField3(100); + + return cls.field1 + cls.field2 + cls.GetField3(); + } + + public static void Main() {} +} \ No newline at end of file diff --git a/dotnet-tests/resources/ObjectInit.cs b/dotnet-tests/resources/ObjectInit.cs new file mode 100644 index 00000000..6353935e --- /dev/null +++ b/dotnet-tests/resources/ObjectInit.cs @@ -0,0 +1,23 @@ +using System; +using Expload.Pravda; + +public class A +{ + private byte b; + private short s; + private double d; + private int i; + private string str; + private Bytes bs; +} + +[Program] +public class ObjectInit +{ + public void TestObjectInit() + { + A a = new A(); + } + + public static void Main() {} +} \ No newline at end of file diff --git a/dotnet-tests/resources/Pcall.cs b/dotnet-tests/resources/Pcall.cs new file mode 100644 index 00000000..7b396d81 --- /dev/null +++ b/dotnet-tests/resources/Pcall.cs @@ -0,0 +1,20 @@ +using System; +using Expload.Pravda; + +namespace PcallNamespace { + + [Program] + public class Pcall + { + public int TestPcall() + { + Bytes address1 = new Bytes("1eaed20b7ce2b336043e4b340b031f95bb1ce6d935ef733ae4df1b66e1e3d91f"); + int a = ProgramHelper.Program(address1).Add(1, 2); + Bytes address2 = new Bytes(new sbyte[] {30, -82, -46, 11, 124, -30, -77, 54, 4, 62, 75, 52, 11, 3, 31, -107, -69, 28, -26, -39, 53, -17, 115, 58, -28, -33, 27, 102, -31, -29, -39, 31}); + int b = ProgramHelper.Program(address2).Add(3, 4); + return a + b; + } + + public static void Main() {} + } +} diff --git a/dotnet-tests/resources/pcall_program.cs b/dotnet-tests/resources/PcallProgram.cs similarity index 52% rename from dotnet-tests/resources/pcall_program.cs rename to dotnet-tests/resources/PcallProgram.cs index f168b827..7153b6fb 100644 --- a/dotnet-tests/resources/pcall_program.cs +++ b/dotnet-tests/resources/PcallProgram.cs @@ -1,11 +1,10 @@ using System; using Expload.Pravda; -// this interface is obligatory for programs that are called with ProgramHelper.Program<...> -namespace Expload.Pravda.Programs -{ +namespace PcallNamespace { + [Program] - public class MyAnotherProgram { + public class PcallProgram { public int Add(int a, int b) { return a + b; diff --git a/dotnet-tests/resources/poker.cs b/dotnet-tests/resources/Poker.cs similarity index 78% rename from dotnet-tests/resources/poker.cs rename to dotnet-tests/resources/Poker.cs index fd4ae9a2..9b58f200 100644 --- a/dotnet-tests/resources/poker.cs +++ b/dotnet-tests/resources/Poker.cs @@ -4,50 +4,50 @@ [Program] class MyProgram { - public Mapping PlayerCards1 = new Mapping(); - public Mapping PlayerCards2 = new Mapping(); - public Mapping TableCards = new Mapping(); - public Mapping Folded = new Mapping(); + private Mapping PlayerCards1 = new Mapping(); + private Mapping PlayerCards2 = new Mapping(); + private Mapping TableCards = new Mapping(); + private Mapping Folded = new Mapping(); - public Mapping Players = new Mapping(); + private Mapping Players = new Mapping(); - public Mapping Bets = new Mapping(); - public Mapping Bankrolls = new Mapping(); + private Mapping Bets = new Mapping(); + private Mapping Bankrolls = new Mapping(); public void Deal(Bytes p1, Bytes p2, Bytes p3, Bytes p4, Bytes p5, Bytes p6, Bytes p7, Bytes p8, Bytes p9) { - TableCards.put(-1, 0); + TableCards[-1] = 0; int len = 0; - if (p1 != Bytes.EMPTY) Players.put(len++, p1); - if (p2 != Bytes.EMPTY) Players.put(len++, p2); - if (p3 != Bytes.EMPTY) Players.put(len++, p3); - if (p4 != Bytes.EMPTY) Players.put(len++, p4); - if (p5 != Bytes.EMPTY) Players.put(len++, p5); - if (p6 != Bytes.EMPTY) Players.put(len++, p6); - if (p7 != Bytes.EMPTY) Players.put(len++, p7); - if (p8 != Bytes.EMPTY) Players.put(len++, p8); - Players.put(-1, new Bytes(Convert.ToByte(len))); + if (p1 != Bytes.EMPTY) Players[len++] = p1; + if (p2 != Bytes.EMPTY) Players[len++] = p2; + if (p3 != Bytes.EMPTY) Players[len++] = p3; + if (p4 != Bytes.EMPTY) Players[len++] = p4; + if (p5 != Bytes.EMPTY) Players[len++] = p5; + if (p6 != Bytes.EMPTY) Players[len++] = p6; + if (p7 != Bytes.EMPTY) Players[len++] = p7; + if (p8 != Bytes.EMPTY) Players[len++] = p8; + Players[-1] = new Bytes(Convert.ToSByte(len)); } public void DealPublicCard(int card) { - int len = TableCards.getDefault(-1, 0); - TableCards.put(len++, card); - TableCards.put(-1, len); + int len = TableCards.GetOrDefault(-1, 0); + TableCards[len++] = card; + TableCards[-1] = len; } public void DealPrivateCard(int player, Bytes cardHash) { - Bytes p = Players.getDefault(player, Bytes.EMPTY); + Bytes p = Players.GetOrDefault(player, Bytes.EMPTY); if (p == Bytes.EMPTY) return; // no such player! - if (PlayerCards1.getDefault(p, Bytes.EMPTY) == Bytes.EMPTY) + if (PlayerCards1.GetOrDefault(p, Bytes.EMPTY) == Bytes.EMPTY) { - PlayerCards1.put(p, cardHash); + PlayerCards1[p] = cardHash; } else { - PlayerCards2.put(p, cardHash); + PlayerCards2[p] = cardHash; } } public string Showdown(string cardSalt, Bytes dealtCards) @@ -56,9 +56,9 @@ public string Showdown(string cardSalt, Bytes dealtCards) } private string showdown(string cardSalt, Bytes dealtCards) { - Bytes playersCountB = Players.getDefault(-1, new Bytes(0)); + Bytes playersCountB = Players.GetOrDefault(-1, new Bytes(0)); int playersCount = playersCountB[0]; - int dealt = playersCount * 2 + TableCards.getDefault(-1, 0); + int dealt = playersCount * 2 + TableCards.GetOrDefault(-1, 0); if (dealt != Convert.ToInt32(dealtCards[0])) // length // should be a built-in error @@ -71,11 +71,11 @@ private string showdown(string cardSalt, Bytes dealtCards) if (i < (playersCount * 2)) { Bytes playerCardHash; - Bytes p = Players.get(i % playersCount); + Bytes p = Players[i % playersCount]; if (i < playersCount) - playerCardHash = PlayerCards1.getDefault(p, Bytes.EMPTY); + playerCardHash = PlayerCards1.GetOrDefault(p, Bytes.EMPTY); else - playerCardHash = PlayerCards2.getDefault(p, Bytes.EMPTY); + playerCardHash = PlayerCards2.GetOrDefault(p, Bytes.EMPTY); if (playerCardHash != cardHash) return "cards mismatch! #" + Convert.ToString(i) + " (" + Convert.ToString(card) + ") expected: " + Convert.ToString(playerCardHash) + " got: " + Convert.ToString(cardHash); @@ -85,28 +85,28 @@ private string showdown(string cardSalt, Bytes dealtCards) int totalWin = 0; for (int i = 0; i < playersCount; i++) { - Bytes player = Players.getDefault(i, Bytes.EMPTY); - int bet = Bets.getDefault(player, 0); + Bytes player = Players.GetOrDefault(i, Bytes.EMPTY); + int bet = Bets.GetOrDefault(player, 0); totalWin = totalWin + bet; - int bank = Bankrolls.getDefault(player, 0); - Bankrolls.put(player, bank - bet); + int bank = Bankrolls.GetOrDefault(player, 0); + Bankrolls[player] = bank - bet; } int maxHand = 0; Bytes winner = Bytes.EMPTY; for (int i = 0; i < playersCount; i++) { - Bytes player = Players.getDefault(i, Bytes.EMPTY); - bool fold = Folded.getDefault(player, false); + Bytes player = Players.GetOrDefault(i, Bytes.EMPTY); + bool fold = Folded.GetOrDefault(player, false); if (!fold) { int[] cards = new int[]{ - Convert.ToInt32(PlayerCards1.getDefault(player, Bytes.EMPTY)[0]), - Convert.ToInt32(PlayerCards2.getDefault(player, Bytes.EMPTY)[0]), - TableCards.getDefault(0, -1), - TableCards.getDefault(1, -1), - TableCards.getDefault(2, -1), - TableCards.getDefault(3, -1), - TableCards.getDefault(4, -1), + Convert.ToInt32(PlayerCards1.GetOrDefault(player, Bytes.EMPTY)[0]), + Convert.ToInt32(PlayerCards2.GetOrDefault(player, Bytes.EMPTY)[0]), + TableCards.GetOrDefault(0, -1), + TableCards.GetOrDefault(1, -1), + TableCards.GetOrDefault(2, -1), + TableCards.GetOrDefault(3, -1), + TableCards.GetOrDefault(4, -1), }; int temp = 0; @@ -137,24 +137,24 @@ private string showdown(string cardSalt, Bytes dealtCards) if (winner == Bytes.EMPTY) return "couldn't find winner!"; - int wbank = Bankrolls.getDefault(winner, 0); - Bankrolls.put(winner, wbank + totalWin); + int wbank = Bankrolls.GetOrDefault(winner, 0); + Bankrolls[winner] = wbank + totalWin; return "success!"; } public void CreatePlayer(Bytes p, int bankroll) { - Bankrolls.put(p, bankroll); + Bankrolls[p] = bankroll; } public string UpdateBet(Bytes p, int bet) { - int old = Bets.getDefault(p, 0); + int old = Bets.GetOrDefault(p, 0); if (bet <= old) return "bet is lower than before! old: " + Convert.ToString(old) + ", new: " + Convert.ToString(bet); - if (bet > Bankrolls.getDefault(p, 0)) - return "not enough bankroll! bet: " + Convert.ToString(bet) + ", bankroll: " + Convert.ToString(Bankrolls.getDefault(p, 0)); + if (bet > Bankrolls.GetOrDefault(p, 0)) + return "not enough bankroll! bet: " + Convert.ToString(bet) + ", bankroll: " + Convert.ToString(Bankrolls.GetOrDefault(p, 0)); - Bets.put(p, bet); + Bets[p] = bet; return "success!"; } @@ -197,23 +197,23 @@ private bool hasFlag(int b1, int b2) // should be bitwise operation return (b1 / b2) % 2 == 1; } - private Bytes selectCombination(int c0, int c1, int c2, int c3, int c4, int c5, int c6, byte comb) + private Bytes selectCombination(int c0, int c1, int c2, int c3, int c4, int c5, int c6, sbyte comb) { Bytes select = Bytes.EMPTY; if (hasFlag(comb, 1)) - select = select.Concat(new Bytes(Convert.ToByte(c0))); + select = select.Concat(new Bytes(Convert.ToSByte(c0))); if (hasFlag(comb, 2)) - select = select.Concat(new Bytes(Convert.ToByte(c1))); + select = select.Concat(new Bytes(Convert.ToSByte(c1))); if (hasFlag(comb, 4)) - select = select.Concat(new Bytes(Convert.ToByte(c2))); + select = select.Concat(new Bytes(Convert.ToSByte(c2))); if (hasFlag(comb, 8)) - select = select.Concat(new Bytes(Convert.ToByte(c3))); + select = select.Concat(new Bytes(Convert.ToSByte(c3))); if (hasFlag(comb, 16)) - select = select.Concat(new Bytes(Convert.ToByte(c4))); + select = select.Concat(new Bytes(Convert.ToSByte(c4))); if (hasFlag(comb, 32)) - select = select.Concat(new Bytes(Convert.ToByte(c5))); + select = select.Concat(new Bytes(Convert.ToSByte(c5))); if (hasFlag(comb, 64)) - select = select.Concat(new Bytes(Convert.ToByte(c6))); + select = select.Concat(new Bytes(Convert.ToSByte(c6))); return select; } diff --git a/dotnet-tests/resources/ProgramFields.cs b/dotnet-tests/resources/ProgramFields.cs new file mode 100644 index 00000000..34fd3f8c --- /dev/null +++ b/dotnet-tests/resources/ProgramFields.cs @@ -0,0 +1,16 @@ +using System; +using Expload.Pravda; + +[Program] +public class ProgramFields { + private sbyte Sbyte; + private short Short; + private int Int; + private double Double; + private string String; + private Bytes Bytess; + private Object Object; + private Mapping Map; + + public static void Main() {} +} \ No newline at end of file diff --git a/dotnet-tests/resources/ProgramInterface.cs b/dotnet-tests/resources/ProgramInterface.cs new file mode 100644 index 00000000..ec52e200 --- /dev/null +++ b/dotnet-tests/resources/ProgramInterface.cs @@ -0,0 +1,22 @@ +using System; +using Expload.Pravda; + +namespace InterfaceNamespace { + + [Program] + public interface ProgramInterface + { + int Add(int a, int b); + } + + [Program] + public class ProgramInterfaceImpl : ProgramInterface + { + public int Add(int a, int b) + { + return a + b; + } + + public static void Main() {} + } +} \ No newline at end of file diff --git a/dotnet-tests/resources/ProgramInterfaceCheck.cs b/dotnet-tests/resources/ProgramInterfaceCheck.cs new file mode 100644 index 00000000..1aa5c54e --- /dev/null +++ b/dotnet-tests/resources/ProgramInterfaceCheck.cs @@ -0,0 +1,17 @@ +using System; +using Expload.Pravda; + +namespace InterfaceNamespace { + + [Program] + public class ProgramInterfaceCheck + { + public int CheckInterface() + { + Bytes address = new Bytes("1eaed20b7ce2b336043e4b340b031f95bb1ce6d935ef733ae4df1b66e1e3d91f"); + return ProgramHelper.Program(address).Add(1, 2); + } + + public static void Main() {} + } +} diff --git a/dotnet-tests/resources/PublicMapping.cs b/dotnet-tests/resources/PublicMapping.cs new file mode 100644 index 00000000..07a458f0 --- /dev/null +++ b/dotnet-tests/resources/PublicMapping.cs @@ -0,0 +1,15 @@ +using System; +using Expload.Pravda; + +[Program] +public class PublicMapping +{ + public int SomeField = 42; + + public void Test() + { + return; + } + + public static void Main() {} +} \ No newline at end of file diff --git a/dotnet-tests/resources/ReturnObject.cs b/dotnet-tests/resources/ReturnObject.cs new file mode 100644 index 00000000..ede50111 --- /dev/null +++ b/dotnet-tests/resources/ReturnObject.cs @@ -0,0 +1,26 @@ +using System; +using Expload.Pravda; + +namespace ReturnObjectNamespace { + + public class SomeObject + { + public int SomeField; + + public SomeObject(int someField) + { + SomeField = someField; + } + } + + [Program] + public class ReturnObject + { + public SomeObject GetObject() + { + return new SomeObject(42); + } + + public static void Main() {} + } +} diff --git a/dotnet-tests/resources/ReturnObjectCheck.cs b/dotnet-tests/resources/ReturnObjectCheck.cs new file mode 100644 index 00000000..47089938 --- /dev/null +++ b/dotnet-tests/resources/ReturnObjectCheck.cs @@ -0,0 +1,17 @@ +using System; +using Expload.Pravda; + +namespace ReturnObjectNamespace { + + [Program] + public class ReturnObjectCheck + { + public int TestReturnObject() + { + Bytes address = new Bytes("123456789012345678901234567890123456789012345678901234567890ABCD"); + return ProgramHelper.Program(address).GetObject().SomeField; + } + + public static void Main() {} + } +} diff --git a/dotnet-tests/resources/SmartProgram.cs b/dotnet-tests/resources/SmartProgram.cs new file mode 100644 index 00000000..9fe1ef93 --- /dev/null +++ b/dotnet-tests/resources/SmartProgram.cs @@ -0,0 +1,32 @@ +using System; +using Expload.Pravda; + +[Program] +public class SmartProgram +{ + private Mapping Balances = new Mapping(); + + public int BalanceOf(Bytes tokenOwner) + { + return Balances.GetOrDefault(tokenOwner, 0); + } + + public void Transfer(Bytes to, int tokens) + { + if (tokens > 0) { + if (Balances.GetOrDefault(Info.Sender(), 0) >= tokens) { + Balances[Info.Sender()] = Balances.GetOrDefault(Info.Sender(), 0) - tokens; + Balances[to] = Balances.GetOrDefault(to, 0) + tokens; + } + } + } + + public void Emit(Bytes owner, int tokens) + { + if (tokens > 0) { + Balances[owner] = Balances.GetOrDefault(owner, 0) + tokens; + } + } + + public static void Main() {} +} \ No newline at end of file diff --git a/dotnet-tests/resources/static_class.cs b/dotnet-tests/resources/StaticClass.cs similarity index 84% rename from dotnet-tests/resources/static_class.cs rename to dotnet-tests/resources/StaticClass.cs index 1a9c27b5..8f36abb3 100644 --- a/dotnet-tests/resources/static_class.cs +++ b/dotnet-tests/resources/StaticClass.cs @@ -1,9 +1,11 @@ using Expload.Pravda; using System; +using StaticClassUtils; -namespace Expload.Pravda +namespace StaticClassUtils { - public static class StringUtils { + public static class StringUtils + { private static string HexPart(int b) { if (b == 0) @@ -41,7 +43,7 @@ private static string HexPart(int b) return ""; } - public static string ByteToHex(byte b) + public static string ByteToHex(int b) { return HexPart(b / 16) + HexPart(b % 16); } @@ -50,7 +52,7 @@ public static string BytesToHex(Bytes bytes) { string res = ""; for (int i = 0; i < bytes.Length(); i++) { - res += ByteToHex(bytes[i]); + res += ByteToHex(bytes[i] & 0xFF); } return res; } @@ -58,8 +60,10 @@ public static string BytesToHex(Bytes bytes) } [Program] -class MyProgram { - public string ToHex(Bytes bs) { +class StaticClass +{ + public string TestToHex(Bytes bs) + { return StringUtils.BytesToHex(bs); } diff --git a/dotnet-tests/resources/stdlib.cs b/dotnet-tests/resources/Stdlib.cs similarity index 74% rename from dotnet-tests/resources/stdlib.cs rename to dotnet-tests/resources/Stdlib.cs index e5cb162b..19d886fe 100644 --- a/dotnet-tests/resources/stdlib.cs +++ b/dotnet-tests/resources/Stdlib.cs @@ -2,7 +2,8 @@ using Expload.Pravda; [Program] -class MyProgram { +public class Stdlib +{ public Bytes Ripemd160(String input) { return StdLib.Ripemd160(input); @@ -13,5 +14,10 @@ public bool ValidateEd25519Signature(Bytes pubKey, String message, Bytes sign) return StdLib.ValidateEd25519Signature(pubKey, message, sign); } + public String BytesToHex(Bytes bytes) + { + return StdLib.BytesToHex(bytes); + } + public static void Main() {} } \ No newline at end of file diff --git a/dotnet-tests/resources/strings.cs b/dotnet-tests/resources/Strings.cs similarity index 54% rename from dotnet-tests/resources/strings.cs rename to dotnet-tests/resources/Strings.cs index 73fc5f96..536220d7 100644 --- a/dotnet-tests/resources/strings.cs +++ b/dotnet-tests/resources/Strings.cs @@ -2,19 +2,20 @@ using Expload.Pravda; [Program] -public class ProgramStrings +public class Strings { - public Mapping strings = new Mapping(); + private Mapping StringsMapping = new Mapping(); - public void distributeSalary() { + public void TestStrings() + { string salary = "za" + "user1"; string us = "us"; string er2 = "er2"; string user = us + er2; - strings.put(user, salary); - if (strings.exists("user1")) { - strings.put("user2", ""); + StringsMapping[user] = salary; + if (StringsMapping.ContainsKey("user1")) { + StringsMapping["user2"] = ""; } char c0 = salary[0]; diff --git a/dotnet-tests/resources/SystemMethods.cs b/dotnet-tests/resources/SystemMethods.cs new file mode 100644 index 00000000..efeb5fa3 --- /dev/null +++ b/dotnet-tests/resources/SystemMethods.cs @@ -0,0 +1,19 @@ +using System; +using Expload.Pravda; + +[Program] +public class SystemMethods +{ + public void TestSystemMethods() + { + long balance = Info.Balance(Bytes.VOID_ADDRESS); + Bytes programAddress = Info.ProgramAddress(); + + Actions.Transfer(Bytes.VOID_ADDRESS, 100L); + Actions.TransferFromProgram(Bytes.VOID_ADDRESS, 200L); + + Bytes nullBytes = null; + } + + public static void Main() {} +} \ No newline at end of file diff --git a/dotnet-tests/resources/vm_ops.cs b/dotnet-tests/resources/VmOps.cs similarity index 66% rename from dotnet-tests/resources/vm_ops.cs rename to dotnet-tests/resources/VmOps.cs index 7f4da3c6..b91a2a1b 100644 --- a/dotnet-tests/resources/vm_ops.cs +++ b/dotnet-tests/resources/VmOps.cs @@ -2,13 +2,12 @@ using Expload.Pravda; [Program] -class MyProgram { - public void Throw() +public class VmOps +{ + public void TestThrow() { Error.Throw("Oops!"); } -} -class MainClass { public static void Main() {} } \ No newline at end of file diff --git a/dotnet-tests/resources/ZooProgram.cs b/dotnet-tests/resources/ZooProgram.cs new file mode 100644 index 00000000..d5e2c4c0 --- /dev/null +++ b/dotnet-tests/resources/ZooProgram.cs @@ -0,0 +1,73 @@ +using System; +using Expload.Pravda; + +[Program] +public class ZooProgram +{ + private Mapping PetToZoo = new Mapping(); + private Mapping PetSignature = new Mapping(); + private Mapping PetToOwner = new Mapping(); + private Mapping ZooToOwner = new Mapping(); + private int ZooCnt = 1; + private int PetId = 1; + + private Bytes GenerateSignature(String pet) + { + sbyte[] sign = new sbyte[10]; + for (int i = 0; i < 10; i++) { + sign[i] = Convert.ToSByte(pet[i % pet.Length] / 2); + } + return new Bytes(sign); + } + + public int NewZoo() + { + ZooToOwner[ZooCnt] = Info.Sender(); + ZooCnt += 1; + return ZooCnt - 1; + } + + public void TransferZoo(Bytes to, int zoo) + { + if (ZooToOwner.GetOrDefault(zoo, Bytes.EMPTY) == Info.Sender()) { + ZooToOwner[zoo] = to; + } + } + + public String NewPet(int zoo) + { + if (ZooToOwner.GetOrDefault(zoo, Bytes.EMPTY) == Info.Sender()) { + String pet = "pet" + System.Convert.ToString(PetId); + PetToOwner[pet] = Info.Sender(); + PetSignature[pet] = GenerateSignature(pet); + PetId += 1; + return pet; + } + return ""; + } + + public void TransferPet(Bytes to, int zoo, String pet) + { + if (PetToOwner.GetOrDefault(pet, Bytes.EMPTY) == Info.Sender() && ZooToOwner.GetOrDefault(zoo, Bytes.EMPTY) == to) { + PetToOwner[pet] = to; + PetToZoo[pet] = zoo; + } + } + + public String BreedPets(String pet1, String pet2) + { + if (PetToOwner.GetOrDefault(pet1, Bytes.EMPTY) == Info.Sender() && + PetToOwner.GetOrDefault(pet2, Bytes.EMPTY) == Info.Sender() && + PetToZoo.GetOrDefault(pet1, -1) == PetToZoo.GetOrDefault(pet2, -1)) { + + String newPet = pet1 + pet2; + PetToOwner[newPet] = Info.Sender(); + PetSignature[newPet] = PetSignature[pet1].Concat(PetSignature[pet2]); + return newPet; + } else { + return ""; + } + } + + public static void Main() {} +} diff --git a/dotnet-tests/resources/arithmetics.cs b/dotnet-tests/resources/arithmetics.cs deleted file mode 100644 index 79fa1fcd..00000000 --- a/dotnet-tests/resources/arithmetics.cs +++ /dev/null @@ -1,12 +0,0 @@ - -public class Program { - public static int x = 10; - - public static void Main() { - int a = x + 2; - int b = x * 2; - int c = x / 2; - int d = x % 2; - int e = ((a + b + 42) * c + d) / 1337; - } -} \ No newline at end of file diff --git a/dotnet-tests/resources/arithmetics.exe b/dotnet-tests/resources/arithmetics.exe deleted file mode 100644 index 0406f246..00000000 Binary files a/dotnet-tests/resources/arithmetics.exe and /dev/null differ diff --git a/dotnet-tests/resources/arrays.exe b/dotnet-tests/resources/arrays.exe deleted file mode 100644 index b54b8963..00000000 Binary files a/dotnet-tests/resources/arrays.exe and /dev/null differ diff --git a/dotnet-tests/resources/closure.cs b/dotnet-tests/resources/closure.cs deleted file mode 100644 index 2e5c11cd..00000000 --- a/dotnet-tests/resources/closure.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System; - -public class Program { - public static void Main() { - int e = 1; - Func d = x => x + e; - int f = d(3); - } -} \ No newline at end of file diff --git a/dotnet-tests/resources/closure.exe b/dotnet-tests/resources/closure.exe deleted file mode 100644 index e68b7d8b..00000000 Binary files a/dotnet-tests/resources/closure.exe and /dev/null differ diff --git a/dotnet-tests/resources/compare.cs b/dotnet-tests/resources/compare.cs deleted file mode 100644 index 0ec3b407..00000000 --- a/dotnet-tests/resources/compare.cs +++ /dev/null @@ -1,89 +0,0 @@ -using Expload.Pravda; - -[Program] -public class ProgramCompare { - public void compare() - { - int a = 1; - int b = 2; - uint c = 3; - uint d = 4; - long e = 5L; - long f = 6L; - - bool tmp = false; - tmp = a == b; - tmp = a == c; - tmp = c == d; - tmp = c == e; - tmp = e == f; - - if (a == b) {} - if (a == c) {} - if (c == d) {} - if (c == e) {} - if (e == f) {} - - tmp = a >= b; - tmp = a >= c; - tmp = c >= d; - tmp = c >= e; - tmp = e >= f; - - if (a >= b) {} - if (a >= c) {} - if (c >= d) {} - if (c >= e) {} - if (e >= f) {} - - tmp = a <= b; - tmp = a <= c; - tmp = c <= d; - tmp = c <= e; - tmp = e <= f; - - if (a <= b) {} - if (a <= c) {} - if (c <= d) {} - if (c <= e) {} - if (e <= f) {} - - tmp = a != b; - tmp = a != c; - tmp = c != d; - tmp = c != e; - tmp = e != f; - - if (a != b) {} - if (a != c) {} - if (c != d) {} - if (c != e) {} - if (e != f) {} - - tmp = a > b; - tmp = a > c; - tmp = c > d; - tmp = c > e; - tmp = e > f; - - if (a > b) {} - if (a > c) {} - if (c > d) {} - if (c > e) {} - if (e > f) {} - - tmp = a < b; - tmp = a < c; - tmp = c < d; - tmp = c < e; - tmp = e < f; - - if (a < b) {} - if (a < c) {} - if (c < d) {} - if (c < e) {} - if (e < f) {} - } - - public static void Main() {} -} \ No newline at end of file diff --git a/dotnet-tests/resources/compare.exe b/dotnet-tests/resources/compare.exe deleted file mode 100644 index 2788a057..00000000 Binary files a/dotnet-tests/resources/compare.exe and /dev/null differ diff --git a/dotnet-tests/resources/error.exe b/dotnet-tests/resources/error.exe deleted file mode 100644 index 4f0eebe5..00000000 Binary files a/dotnet-tests/resources/error.exe and /dev/null differ diff --git a/dotnet-tests/resources/error.pdb b/dotnet-tests/resources/error.pdb deleted file mode 100644 index 8135e12b..00000000 Binary files a/dotnet-tests/resources/error.pdb and /dev/null differ diff --git a/dotnet-tests/resources/event.exe b/dotnet-tests/resources/event.exe deleted file mode 100644 index 5ad81d3f..00000000 Binary files a/dotnet-tests/resources/event.exe and /dev/null differ diff --git a/dotnet-tests/resources/hello_world.cs b/dotnet-tests/resources/hello_world.cs deleted file mode 100644 index a889db37..00000000 --- a/dotnet-tests/resources/hello_world.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System; - -public class HelloWorld -{ - static public void Main () - { - Console.WriteLine("Hello World!"); - } -} \ No newline at end of file diff --git a/dotnet-tests/resources/hello_world.exe b/dotnet-tests/resources/hello_world.exe deleted file mode 100644 index d538e2c8..00000000 Binary files a/dotnet-tests/resources/hello_world.exe and /dev/null differ diff --git a/dotnet-tests/resources/hello_world.pdb b/dotnet-tests/resources/hello_world.pdb deleted file mode 100644 index 0719c554..00000000 Binary files a/dotnet-tests/resources/hello_world.pdb and /dev/null differ diff --git a/dotnet-tests/resources/if.exe b/dotnet-tests/resources/if.exe deleted file mode 100644 index ea7b76b6..00000000 Binary files a/dotnet-tests/resources/if.exe and /dev/null differ diff --git a/dotnet-tests/resources/inheritance.exe b/dotnet-tests/resources/inheritance.exe deleted file mode 100644 index 70c213ac..00000000 Binary files a/dotnet-tests/resources/inheritance.exe and /dev/null differ diff --git a/dotnet-tests/resources/loop.exe b/dotnet-tests/resources/loop.exe deleted file mode 100644 index d134e6d9..00000000 Binary files a/dotnet-tests/resources/loop.exe and /dev/null differ diff --git a/dotnet-tests/resources/loop_nested.exe b/dotnet-tests/resources/loop_nested.exe deleted file mode 100644 index 002aa2da..00000000 Binary files a/dotnet-tests/resources/loop_nested.exe and /dev/null differ diff --git a/dotnet-tests/resources/method_calling.cs b/dotnet-tests/resources/method_calling.cs deleted file mode 100644 index 722bde3d..00000000 --- a/dotnet-tests/resources/method_calling.cs +++ /dev/null @@ -1,32 +0,0 @@ - -public class Program { - public static int answer() { - return 42; - } - - private static int secretAnswer() { - return 40 + 2; - } - - public static int sum(int a, int b) { - return a + b; - } - - public int personalAnswer() { - return 21 * 2; - } - - private int personalSecretAnswer() { - return 126 / 3; - } - - public static void Main() { - int a = Program.answer(); - int b = Program.secretAnswer(); - int c = Program.sum(a, b); - - Program p = new Program(); - int d = p.personalAnswer(); - int e = p.personalSecretAnswer(); - } -} \ No newline at end of file diff --git a/dotnet-tests/resources/method_calling.exe b/dotnet-tests/resources/method_calling.exe deleted file mode 100644 index 837a1346..00000000 Binary files a/dotnet-tests/resources/method_calling.exe and /dev/null differ diff --git a/dotnet-tests/resources/objects.exe b/dotnet-tests/resources/objects.exe deleted file mode 100644 index 31fb5db9..00000000 Binary files a/dotnet-tests/resources/objects.exe and /dev/null differ diff --git a/dotnet-tests/resources/pcall.cs b/dotnet-tests/resources/pcall.cs deleted file mode 100644 index faf64c30..00000000 --- a/dotnet-tests/resources/pcall.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; -using Expload.Pravda; - -namespace Expload.Pravda.Programs -{ - [Program] - public class MyProgram { - public int pcall() { - Bytes address1 = new Bytes("1eaed20b7ce2b336043e4b340b031f95bb1ce6d935ef733ae4df1b66e1e3d91f"); - int a = ProgramHelper.Program(address1).Add(1, 2); - Bytes address2 = new Bytes(new byte[] {30, 174, 210, 11, 124, 226, 179, 54, 4, 62, 75, 52, 11, 3, 31, 149, 187, 28, 230, 217, 53, 239, 115, 58, 228, 223, 27, 102, 225, 227, 217, 31}); - int b = ProgramHelper.Program(address2).Add(3, 4); - return a + b; - } - - public static void Main() {} - } -} diff --git a/dotnet-tests/resources/pcall.exe b/dotnet-tests/resources/pcall.exe deleted file mode 100644 index 7dbd3b21..00000000 Binary files a/dotnet-tests/resources/pcall.exe and /dev/null differ diff --git a/dotnet-tests/resources/pcall_program.dll b/dotnet-tests/resources/pcall_program.dll deleted file mode 100644 index 95d81bc0..00000000 Binary files a/dotnet-tests/resources/pcall_program.dll and /dev/null differ diff --git a/dotnet-tests/resources/smart_program.cs b/dotnet-tests/resources/smart_program.cs deleted file mode 100644 index dbf2f288..00000000 --- a/dotnet-tests/resources/smart_program.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; -using Expload.Pravda; - -[Program] -class MyProgram { - Mapping balances = new Mapping(); - - public int balanceOf(Bytes tokenOwner) { - return balances.getDefault(tokenOwner, 0); - } - - public void transfer(Bytes to, int tokens) { - if (tokens > 0) { - if (balances.getDefault(Info.Sender(), 0) >= tokens) { - balances.put(Info.Sender(), balances.getDefault(Info.Sender(), 0) - tokens); - balances.put(to, balances.getDefault(to, 0) + tokens); - } - } - } - - public static void Main() {} -} \ No newline at end of file diff --git a/dotnet-tests/resources/smart_program.exe b/dotnet-tests/resources/smart_program.exe deleted file mode 100644 index 81c3261c..00000000 Binary files a/dotnet-tests/resources/smart_program.exe and /dev/null differ diff --git a/dotnet-tests/resources/smart_program.pdb b/dotnet-tests/resources/smart_program.pdb deleted file mode 100644 index 6bfd8cb2..00000000 Binary files a/dotnet-tests/resources/smart_program.pdb and /dev/null differ diff --git a/dotnet-tests/resources/static_class.exe b/dotnet-tests/resources/static_class.exe deleted file mode 100644 index 3b7af90f..00000000 Binary files a/dotnet-tests/resources/static_class.exe and /dev/null differ diff --git a/dotnet-tests/resources/stdlib.exe b/dotnet-tests/resources/stdlib.exe deleted file mode 100644 index 42df427b..00000000 Binary files a/dotnet-tests/resources/stdlib.exe and /dev/null differ diff --git a/dotnet-tests/resources/strings.exe b/dotnet-tests/resources/strings.exe deleted file mode 100644 index a78f8fdd..00000000 Binary files a/dotnet-tests/resources/strings.exe and /dev/null differ diff --git a/dotnet-tests/resources/system.cs b/dotnet-tests/resources/system.cs deleted file mode 100644 index c657ed67..00000000 --- a/dotnet-tests/resources/system.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using Expload.Pravda; - -[Program] -class MyProgram { - public void system() { - long balance = Info.Balance(Bytes.EMPTY); - Bytes voidAddress = Bytes.VOID_ADDRESS; - Bytes programAddress = Info.ProgramAddress(); - } - - public static void Main() {} -} \ No newline at end of file diff --git a/dotnet-tests/resources/system.exe b/dotnet-tests/resources/system.exe deleted file mode 100644 index d3ae136d..00000000 Binary files a/dotnet-tests/resources/system.exe and /dev/null differ diff --git a/dotnet-tests/resources/zoo_program.cs b/dotnet-tests/resources/zoo_program.cs deleted file mode 100644 index c637fc75..00000000 --- a/dotnet-tests/resources/zoo_program.cs +++ /dev/null @@ -1,72 +0,0 @@ -using System; -using Expload.Pravda; - -[Program] -class ZooProgram { - public Mapping PetToZoo = new Mapping(); - public Mapping PetSignature = new Mapping(); - public Mapping PetToOwner = new Mapping(); - public Mapping ZooToOwner = new Mapping(); - public int ZooCnt = 1; - public int PetId = 1; - - private Bytes GenerateSignature(String pet) - { - byte[] sign = new byte[10]; - for (int i = 0; i < 10; i++) { - sign[i] = Convert.ToByte(pet[i % pet.Length] / 2); - } - return new Bytes(sign); - } - - public int NewZoo() - { - ZooToOwner.put(ZooCnt, Info.Sender()); - ZooCnt += 1; - return ZooCnt - 1; - } - - public void TransferZoo(Bytes to, int zoo) - { - if (ZooToOwner.getDefault(zoo, Bytes.EMPTY) == Info.Sender()) { - ZooToOwner.put(zoo, to); - } - } - - public String NewPet(int zoo) - { - if (ZooToOwner.getDefault(zoo, Bytes.EMPTY) == Info.Sender()) { - String pet = "pet" + System.Convert.ToString(PetId); - PetToOwner.put(pet, Info.Sender()); - PetSignature.put(pet, GenerateSignature(pet)); - PetId += 1; - return pet; - } - return ""; - } - - public void TransferPet(Bytes to, int zoo, String pet) - { - if (PetToOwner.getDefault(pet, Bytes.EMPTY) == Info.Sender() && ZooToOwner.getDefault(zoo, Bytes.EMPTY) == to) { - PetToOwner.put(pet, to); - PetToZoo.put(pet, zoo); - } - } - - public String BreedPets(String pet1, String pet2) - { - if (PetToOwner.getDefault(pet1, Bytes.EMPTY) == Info.Sender() && - PetToOwner.getDefault(pet2, Bytes.EMPTY) == Info.Sender() && - PetToZoo.getDefault(pet1, -1) == PetToZoo.getDefault(pet2, -1)) { - - String newPet = pet1 + pet2; - PetToOwner.put(newPet, Info.Sender()); - PetSignature.put(newPet, PetSignature.get(pet1).Concat(PetSignature.get(pet2))); - return newPet; - } else { - return ""; - } - } - - public static void Main() {} -} diff --git a/dotnet-tests/resources/zoo_program.exe b/dotnet-tests/resources/zoo_program.exe deleted file mode 100644 index fb5bcfd6..00000000 Binary files a/dotnet-tests/resources/zoo_program.exe and /dev/null differ diff --git a/dotnet/src/main/resources/stdlib.cs b/dotnet/src/main/resources/stdlib.cs deleted file mode 100644 index ae8ca679..00000000 --- a/dotnet/src/main/resources/stdlib.cs +++ /dev/null @@ -1,57 +0,0 @@ -using Com.Expload; -using System; - -namespace Com.Expload -{ - public static class StringUtils { - private static string HexPart(byte b) - { - if (b == 0) - return "0"; - else if (b == 1) - return "1"; - else if (b == 2) - return "2"; - else if (b == 3) - return "3"; - else if (b == 4) - return "4"; - else if (b == 5) - return "5"; - else if (b == 6) - return "6"; - else if (b == 7) - return "7"; - else if (b == 8) - return "8"; - else if (b == 9) - return "9"; - else if (b == 10) - return "A"; - else if (b == 11) - return "B"; - else if (b == 12) - return "C"; - else if (b == 13) - return "D"; - else if (b == 14) - return "E"; - else if (b == 15) - return "F"; - } - - public static string ByteToHex(byte b) - { - return HexPart(b / 16) + HexPart(b % 16); - } - - public static string BytesToHex(Bytes bytes) - { - string res = ""; - for (int i = 0; i < bytes.Length; i++) { - res += BytesToHex(bytes[i]); - } - return res; - } - } -} \ No newline at end of file diff --git a/dotnet/src/main/scala/pravda/dotnet/data/TablesData.scala b/dotnet/src/main/scala/pravda/dotnet/data/TablesData.scala index 973ef1e6..d593e3f9 100644 --- a/dotnet/src/main/scala/pravda/dotnet/data/TablesData.scala +++ b/dotnet/src/main/scala/pravda/dotnet/data/TablesData.scala @@ -75,7 +75,7 @@ object TablesData { params: Vector[ParamData]) extends MethodRefDefData final case class MemberRefData(tableRowData: TableRowData, name: String, signatureIdx: Long) extends MethodRefDefData - final case class FieldData(flags: Short, name: String, signatureIdx: Long) extends TableRowData + final case class FieldData(id: Int, flags: Short, name: String, signatureIdx: Long) extends TableRowData final case class FieldRVAData(field: FieldData, rva: Long) extends TableRowData final case class ParamData(flags: Short, seq: Int, name: String) extends TableRowData final case class TypeDefData(id: Int, @@ -127,11 +127,11 @@ object TablesData { } yield ParamData(flags, seq, name) }.sequence - val fieldListV = peData.tables.fieldTable.map { - case FieldRow(flags, nameIdx, signatureIdx) => + val fieldListV = peData.tables.fieldTable.zipWithIndex.map { + case (FieldRow(flags, nameIdx, signatureIdx), i) => for { name <- Heaps.string(peData.stringHeap, nameIdx) - } yield FieldData(flags, name, signatureIdx) + } yield FieldData(i, flags, name, signatureIdx) }.sequence val fieldRVAListV = peData.tables.fieldRVATable.map { diff --git a/dotnet/src/main/scala/pravda/dotnet/translation/Translator.scala b/dotnet/src/main/scala/pravda/dotnet/translation/Translator.scala index 08168a7a..7f502d24 100644 --- a/dotnet/src/main/scala/pravda/dotnet/translation/Translator.scala +++ b/dotnet/src/main/scala/pravda/dotnet/translation/Translator.scala @@ -18,8 +18,11 @@ package pravda.dotnet.translation import cats.instances.list._ +import cats.instances.vector._ import cats.instances.either._ +import cats.instances.option._ import cats.syntax.traverse._ +import com.google.protobuf.ByteString import pravda.dotnet.data.Method import pravda.dotnet.data.TablesData._ import pravda.dotnet.parser.CIL @@ -27,7 +30,7 @@ import pravda.dotnet.parser.FileParser.{ParsedDotnetFile, ParsedPdb, ParsedPe} import pravda.dotnet.parser.Signatures._ import pravda.dotnet.translation.data._ import pravda.dotnet.translation.jumps.{BranchTransformer, StackOffsetResolver} -import pravda.dotnet.translation.opcode.{CallsTranslation, FieldsTranslation, OpcodeTranslator, StdlibAsm} +import pravda.dotnet.translation.opcode.{CallsTranslation, OpcodeTranslator, StdlibAsm} import pravda.vm.asm.Operation import pravda.vm.{Data, Meta, Opcodes} @@ -38,17 +41,19 @@ object Translator { final val CILMark = Meta.Custom("CIL") - def dotnetToVmTpe(sigType: SigType): Meta.TypeSignature = sigType match { - case SigType.Void => Meta.TypeSignature.Null - case SigType.Boolean => Meta.TypeSignature.Boolean - case SigType.I1 => Meta.TypeSignature.Int8 - case SigType.I2 => Meta.TypeSignature.Int16 - case SigType.I4 => Meta.TypeSignature.Int32 - case SigType.U1 => Meta.TypeSignature.Uint8 - case SigType.U2 => Meta.TypeSignature.Uint16 - case SigType.U4 => Meta.TypeSignature.Uint32 - case TypeDetectors.Bytes() => Meta.TypeSignature.Bytes - case SigType.String => Meta.TypeSignature.Utf8 + def dotnetToVmTpe(sigType: SigType): Option[Meta.TypeSignature] = sigType match { + case SigType.Void => Some(Meta.TypeSignature.Null) + case SigType.Boolean => Some(Meta.TypeSignature.Boolean) + case SigType.I1 => Some(Meta.TypeSignature.Int8) + case SigType.I2 => Some(Meta.TypeSignature.Int16) + case SigType.I4 => Some(Meta.TypeSignature.Int32) + case SigType.I8 => Some(Meta.TypeSignature.Int64) + case SigType.U1 => Some(Meta.TypeSignature.Int16) + case SigType.U2 => Some(Meta.TypeSignature.Int32) + case SigType.U4 => Some(Meta.TypeSignature.Int64) + case TypeDetectors.Bytes() => Some(Meta.TypeSignature.Bytes) + case SigType.String => Some(Meta.TypeSignature.Utf8) + case _ => None // TODO add more types } @@ -58,14 +63,14 @@ object Translator { val methodNames = typeDefData.methods.flatMap { case m @ MethodDefData(_, _, flags, name, sigIdx, _) => if (MethodExtractors.isVirtual(m)) { - Some(CallsTranslation.fullMethodName(name, tctx.signatures.get(sigIdx))) + Some(NamesBuilder.fullMethod(name, tctx.signatures.get(sigIdx))) } else { None } } val notUsedMethods = methodNames.filter(name => !usedMethods.contains(name)) - val notUsedMethodsWithType = notUsedMethods.map((_, CallsTranslation.fullTypeDefName(typeDefData))) + val notUsedMethodsWithType = notUsedMethods.map((_, NamesBuilder.fullTypeDef(typeDefData))) typeDefData.parent match { case typeDef: TypeDefData => @@ -84,6 +89,51 @@ object Translator { }.toList } + private def defaultFieldValue(f: FieldData, tctx: TranslationCtx): Data.Primitive = + (for { + sig <- tctx.signatures.get(f.signatureIdx) + } yield { + sig match { + case FieldSig(tpe) => + tpe match { + case SigType.Boolean => Data.Primitive.Bool.False + case SigType.I1 => Data.Primitive.Int8(0) + case SigType.I2 => Data.Primitive.Int16(0) + case SigType.I4 => Data.Primitive.Int32(0) + case SigType.I8 => Data.Primitive.Int64(0L) + case SigType.U1 => Data.Primitive.Int16(0) + case SigType.U2 => Data.Primitive.Int32(0) + case SigType.U4 => Data.Primitive.Int64(0L) + case SigType.R4 => Data.Primitive.Number(0.0) + case SigType.R8 => Data.Primitive.Number(0.0) + case TypeDetectors.Bytes() => Data.Primitive.Bytes(ByteString.EMPTY) + case SigType.String => Data.Primitive.Utf8("") + case _ => Data.Primitive.Null + } + case _ => Data.Primitive.Null + } + }).getOrElse(Data.Primitive.Null) + + private def initStructFields(typeDefData: TypeDefData, tctx: TranslationCtx): List[Operation] = + typeDefData.fields.toList.flatMap( + f => + List(Operation(Opcodes.DUP), + Operation.Push(defaultFieldValue(f, tctx)), + Operation.StructMut(Some(Data.Primitive.Utf8(f.name))))) + + private def initProgramFields(typeDefData: TypeDefData, tctx: TranslationCtx): List[Operation] = { + typeDefData.fields.toList + .filterNot(f => + tctx.signatures.get(f.signatureIdx).exists { + case FieldSig(SigType.Generic(TypeDetectors.Mapping(), _)) => true + case _ => false + }) + .flatMap(f => + List(Operation.Push(defaultFieldValue(f, tctx)), + Operation.Push(Data.Primitive.Utf8(s"p_${f.name}")), + Operation(Opcodes.SPUT))) + } + private def eliminateDeadFuncs(methods: List[MethodTranslation], funcs: List[MethodTranslation]): List[MethodTranslation] = { @@ -112,6 +162,55 @@ object Translator { funcs.filter(f => all.contains(f.label)) } + private def inspectProgramTypeDef(typeDef: TypeDefData, tctx: TranslationCtx): Either[TranslationError, Unit] = { + lazy val staticFields = { + val staticFieldO = typeDef.fields.find(f => FieldExtractors.isStatic(f.flags)) + staticFieldO match { + case Some(f) => + Left( + TranslationError(InternalError(s"[Program] must not contain static fields: " + + s"${NamesBuilder.fullTypeDef(typeDef)} contains static ${f.name}"), + None)) + case None => + Right(()) + } + } + + lazy val privateFields = typeDef.fields.map { f => + if (!FieldExtractors.isPrivate(f)) { + Left(TranslationError( + InternalError( + s"All [Program] fields must be private: ${f.name} in ${NamesBuilder.fullTypeDef(typeDef)} is not private"), + None)) + } else { + Right(()) + } + }.sequence + + for { + _ <- staticFields + _ <- privateFields + } yield () + } + + private def inspectStructTypeDef(typeDef: TypeDefData, tctx: TranslationCtx): Either[TranslationError, Unit] = { + typeDef.fields + .map { f => + val isMapping = for { + parentSig <- tctx.signatures.get(f.signatureIdx) + } yield CallsTranslation.detectMapping(parentSig) + + if (isMapping.getOrElse(false)) { + Left(TranslationError(InternalError(s"User defined classes must not contain Mappings: ${NamesBuilder + .fullTypeDef(typeDef)} contains ${f.name}"), None)) + } else { + Right(()) + } + } + .sequence + .map(_ => ()) + } + private def translateMethod(cil: List[CIL.Op], name: String, kind: String, @@ -190,12 +289,14 @@ object Translator { methodSign <- tctx.signatures.get(tctx.methodRow(id).signatureIdx) methodTpe <- MethodExtractors.methodType(methodSign) argTpes <- MethodExtractors.methodParams(methodSign) + dotnetMethodTpe <- dotnetToVmTpe(methodTpe) + dotnetArgTpes <- argTpes.map(tpe => dotnetToVmTpe(tpe.tpe)).sequence } yield Operation.Meta( Meta.MethodSignature( name, - dotnetToVmTpe(methodTpe), - argTpes.map(tpe => dotnetToVmTpe(tpe.tpe)) + dotnetMethodTpe, + dotnetArgTpes ) ) } else { @@ -222,17 +323,18 @@ object Translator { ) } - def translateVerbose(files: List[ParsedDotnetFile], + def translateVerbose(rawFiles: List[ParsedDotnetFile], mainClass: Option[String]): Either[TranslationError, Translation] = { - val filesToProgramClasses = files.flatMap(f => + val files = + rawFiles.filterNot(f => f.parsedPdb.exists(_.tablesData.documentTable.exists(_.path.endsWith("Pravda.cs")))) + + val programClasses = files.flatMap(f => f.parsedPe.cilData.tables.customAttributeTable.collect { case CustomAttributeData(td: TypeDefData, MemberRefData(TypeRefData(_, "Program", "Expload.Pravda"), ".ctor", _)) => - f -> td + td }) - val programClasses = filesToProgramClasses.map(_._2) - val mainProgramClassE = mainClass match { case None => if (programClasses.length != 1) { @@ -242,40 +344,61 @@ object Translator { } case Some(name) => programClasses - .find(cls => CallsTranslation.fullTypeDefName(cls) == name) + .find(cls => NamesBuilder.fullTypeDef(cls) == name) .toRight(TranslationError(InternalError(s"Unable to find $name class with [Program] attribute"), None)) } + val structs = files.flatMap(f => f.parsedPe.cilData.tables.typeDefTable).filterNot(programClasses.contains) + + val methodIndexes = TypeDefInvertedFileIndex[MethodDefData]( + files, + (td: TypeDefData) => td.methods, + (f: ParsedDotnetFile, m: MethodDefData) => + NamesBuilder.fullMethod(m.name, f.parsedPe.signatures.get(m.signatureIdx)) + ) + + val fieldIndexes = TypeDefInvertedFileIndex[FieldData](files, + (td: TypeDefData) => td.fields, + (_: ParsedDotnetFile, f: FieldData) => f.name) + mainProgramClassE.flatMap { mainCls => val translations = for { - (f, cls) <- filesToProgramClasses + ((f, methodIndex), fieldIndex) <- files.zip(methodIndexes).zip(fieldIndexes) } yield { - if (cls.fields.exists(f => FieldsTranslation.isStatic(f.flags))) { - Left(TranslationError(InternalError("Static fields in [Program] class are forbidden"), None)) - } else { - val tables = f.parsedPe.cilData.tables - val methodsToTypes: Map[Int, TypeDefData] = tables.methodDefTable.zipWithIndex.flatMap { - case (m, i) => tables.typeDefTable.find(_.methods.exists(_ == m)).map(i -> _) - }.toMap - - val translationCtx = TranslationCtx(f.parsedPe.signatures, - f.parsedPe.cilData, - mainCls, - programClasses, - methodsToTypes, - f.parsedPdb.map(_.tablesData)) - translateAllMethods(f.parsedPe.methods, translationCtx) - } + + val translationCtx = TranslationCtx( + f.parsedPe.signatures, + f.parsedPe.cilData, + mainCls, + programClasses, + structs, + methodIndex, + fieldIndex, + f.parsedPdb.map(_.tablesData) + ) + + val typeDefs = f.parsedPe.cilData.tables.typeDefTable + + for { + _ <- typeDefs.filter(programClasses.contains).map(td => inspectProgramTypeDef(td, translationCtx)).sequence + _ <- typeDefs.filterNot(programClasses.contains).map(td => inspectStructTypeDef(td, translationCtx)).sequence + res <- translateAllMethods(f.parsedPe.methods, translationCtx) + } yield res } - (translations.sequence: Either[TranslationError, List[Translation]]).map(_.reduce[Translation] { - case (Translation(methods1, funcs1), Translation(methods2, funcs2)) => - Translation(methods1 ++ methods2, funcs1 ++ funcs2) - }) + val all = + (translations.sequence: Either[TranslationError, List[FileTranslation]]).map(_.reduce[FileTranslation] { + case (FileTranslation(methods1, funcs1), FileTranslation(methods2, funcs2)) => + FileTranslation(methods1 ++ methods2, funcs1 ++ funcs2) + }) + + val clearedAll = all.map(a => a.copy(funcs = eliminateDeadFuncs(a.methods, a.funcs ++ StdlibAsm.stdlibFuncs))) + + clearedAll.map(Translation(_, NamesBuilder.fullTypeDef(mainCls).replace(".", ""))) } } - def translateAllMethods(methods: List[Method], tctx: TranslationCtx): Either[TranslationError, Translation] = { + def translateAllMethods(methods: List[Method], tctx: TranslationCtx): Either[TranslationError, FileTranslation] = { def withMethodTable(isFunc: MethodDefData => Boolean): Int => Boolean = i => isFunc(tctx.methodRow(i)) @@ -302,9 +425,9 @@ object Translator { val programMethodsFuncsE = for { methodsFuncs <- filterValidateMethods( - i => tctx.isMainProgramMethod(i) && !isCtor(i) && !isCctor(i) && !isMain(i), - i => !isStatic(i) && (isPrivate(i) || isPublic(i)), - InternalError("Only public or private non-static methods are allowed") + i => tctx.isMainProgramMethod(i) && !isCtor(i) && !isCctor(i) && !isMain(i) && !isStatic(i), + i => isPrivate(i) || isPublic(i), + InternalError("Only public or private methods are allowed") ) _ <- { val names = methodsFuncs.map(i => tctx.methodRow(i).name) @@ -335,9 +458,9 @@ object Translator { } } yield ctor - val structEntitiesE = for { - methods <- filterMethods(i => !tctx.isProgramMethod(i) && !isMain(i)) - } yield methods + val programStaticFuncsE = filterMethods(i => tctx.isProgramMethod(i) && !tctx.isMainProgramMethod(i) && isStatic(i)) + + val structEntitiesE = filterMethods(i => !tctx.isProgramMethod(i) && !isMain(i)) val structFuncsE = structEntitiesE.map(_.filter(i => !isCtor(i) && !isCctor(i) && !isStatic(i))) val structStaticFuncsE = structEntitiesE.map(_.filter(i => !isCtor(i) && !isCctor(i) && isStatic(i))) @@ -360,7 +483,7 @@ object Translator { Operation.Push(Data.Primitive.Null), Operation.Push(Data.Primitive.Utf8("init")), Operation(Opcodes.SPUT) - ) + ) ++ initProgramFields(tctx.mainProgramClass, tctx) translateMethod( BranchTransformer.transformBranches(method.opcodes, "ctor"), @@ -436,15 +559,45 @@ object Translator { .sequence } yield opss + lazy val programStaticFuncsOpsE: Either[TranslationError, List[MethodTranslation]] = for { + programStaticFuncs <- programStaticFuncsE + opss <- programStaticFuncs + .map(i => { + val method = methods(i) + val methodRow = tctx.methodRow(i) + val methodName = NamesBuilder.fullMethod(methodRow.name, tctx.signatures.get(methodRow.signatureIdx)) + val tpe = tctx.methodIndex.parent(i).get + val structName = NamesBuilder.fullTypeDef(tpe) + val name = s"$structName.$methodName" + + translateMethod( + BranchTransformer.transformBranches(method.opcodes, name), + name, + "func", + i, + methodRow.params.length, + MethodExtractors.localVariables(method, tctx.signatures).fold(0)(_.length), + void = MethodExtractors.isVoid(methodRow, tctx.signatures), + func = true, + static = true, + struct = None, + forceAdd = false, + tctx.pdbTables.map(_.methodDebugInformationTable(i)), + tctx + ) + }) + .sequence + } yield opss + lazy val structFuncsOpsE: Either[TranslationError, List[MethodTranslation]] = for { structFuncs <- structFuncsE opss <- structFuncs .map(i => { val method = methods(i) val methodRow = tctx.methodRow(i) - val methodName = CallsTranslation.fullMethodName(methodRow.name, tctx.signatures.get(methodRow.signatureIdx)) - val tpe = tctx.methodsToTypes(i) - val structName = CallsTranslation.fullTypeDefName(tpe) + val methodName = NamesBuilder.fullMethod(methodRow.name, tctx.signatures.get(methodRow.signatureIdx)) + val tpe = tctx.methodIndex.parent(i).get + val structName = NamesBuilder.fullTypeDef(tpe) val name = s"$structName.$methodName" translateMethod( @@ -472,9 +625,9 @@ object Translator { .map(i => { val method = methods(i) val methodRow = tctx.methodRow(i) - val methodName = CallsTranslation.fullMethodName(methodRow.name, tctx.signatures.get(methodRow.signatureIdx)) - val tpe = tctx.methodsToTypes(i) - val structName = CallsTranslation.fullTypeDefName(tpe) + val methodName = NamesBuilder.fullMethod(methodRow.name, tctx.signatures.get(methodRow.signatureIdx)) + val tpe = tctx.methodIndex.parent(i).get + val structName = NamesBuilder.fullTypeDef(tpe) val name = s"$structName.$methodName" translateMethod( @@ -502,22 +655,12 @@ object Translator { .flatMap(i => { val method = methods(i) val methodRow = tctx.methodRow(i) - val methodName = CallsTranslation.fullMethodName(methodRow.name, tctx.signatures.get(methodRow.signatureIdx)) - val tpe = tctx.methodsToTypes(i) - val structName = CallsTranslation.fullTypeDefName(tpe) + val methodName = NamesBuilder.fullMethod(methodRow.name, tctx.signatures.get(methodRow.signatureIdx)) + val tpe = tctx.methodIndex.parent(i).get + val structName = NamesBuilder.fullTypeDef(tpe) val name = s"$structName.$methodName" List( - Right( - MethodTranslation( - "vtable", - structName, - forceAdd = false, - List( - OpCodeTranslation(List.empty, - vtableInit(tpe, tctx) :+ - Operation(Opcodes.RET))) - )), translateMethod( BranchTransformer.transformBranches(method.opcodes, name), name, @@ -538,23 +681,47 @@ object Translator { .sequence } yield opss + lazy val structCtorHelpers: List[MethodTranslation] = tctx.structs.flatMap { s => + List( + MethodTranslation( + "vtable", + NamesBuilder.fullTypeDef(s), + forceAdd = false, + List( + OpCodeTranslation(List.empty, + vtableInit(s, tctx) :+ + Operation(Opcodes.RET))) + ), + MethodTranslation( + "default_fields", + NamesBuilder.fullTypeDef(s), + forceAdd = false, + List( + OpCodeTranslation(List.empty, + initStructFields(s, tctx) :+ + Operation(Opcodes.RET))) + ) + ) + } + for { programMethodsOps <- programMethodsOpsE ctorOps <- ctorOpsE programFuncOps <- programFuncsOpsE + programStaticFuncOps <- programStaticFuncsOpsE structFuncsOps <- structFuncsOpsE strucStaticFuncsOps <- structStaticFuncOpsE structCtorsOps <- structCtorsOpsE } yield { val methodsOps = ctorOps ++ programMethodsOps - val funcsOps = programFuncOps ++ structFuncsOps ++ strucStaticFuncsOps ++ structCtorsOps - Translation(methodsOps, eliminateDeadFuncs(methodsOps, funcsOps ++ StdlibAsm.stdlibFuncs)) + val funcsOps = programFuncOps ++ programStaticFuncOps ++ structFuncsOps ++ strucStaticFuncsOps ++ structCtorsOps ++ structCtorHelpers + FileTranslation(methodsOps, funcsOps) } } def translationToAsm(t: Translation): List[Operation] = { - val jumpToMethods = t.methods + val jumpToMethods = t.file.methods .filter(_.name != "ctor") .sortBy(_.name) .flatMap( @@ -578,7 +745,7 @@ object Translator { Operation(Opcodes.THROW) ) - val prefix = Operation.Meta(CILMark) :: ctorCheck ++ + val prefix = Operation.Meta(CILMark) :: Operation.Meta(Meta.ProgramName(t.programName)) :: ctorCheck ++ List(Operation.Label("methods")) ++ jumpToMethods ++ List( Operation.Push(Data.Primitive.Utf8("Wrong method name")), Operation(Opcodes.THROW) @@ -590,8 +757,8 @@ object Translator { } prefix ++ - t.methods.sortBy(_.name).flatMap(m => Operation.Label(m.label) :: m.opcodes.flatMap(opcodeToAsm)) ++ - t.funcs.sortBy(_.label).flatMap(f => Operation.Label(f.label) :: f.opcodes.flatMap(opcodeToAsm)) ++ + t.file.methods.sortBy(_.name).flatMap(m => Operation.Label(m.label) :: m.opcodes.flatMap(opcodeToAsm)) ++ + t.file.funcs.sortBy(_.label).flatMap(f => Operation.Label(f.label) :: f.opcodes.flatMap(opcodeToAsm)) ++ List(Operation.Label("stop")) } diff --git a/dotnet/src/main/scala/pravda/dotnet/translation/data/Translation.scala b/dotnet/src/main/scala/pravda/dotnet/translation/data/FileTranslation.scala similarity index 85% rename from dotnet/src/main/scala/pravda/dotnet/translation/data/Translation.scala rename to dotnet/src/main/scala/pravda/dotnet/translation/data/FileTranslation.scala index be8830bf..a20c67f3 100644 --- a/dotnet/src/main/scala/pravda/dotnet/translation/data/Translation.scala +++ b/dotnet/src/main/scala/pravda/dotnet/translation/data/FileTranslation.scala @@ -25,4 +25,6 @@ final case class MethodTranslation(kind: String, name: String, forceAdd: Boolean lazy val label: String = s"${kind}_$name" } -final case class Translation(methods: List[MethodTranslation], funcs: List[MethodTranslation]) +final case class FileTranslation(methods: List[MethodTranslation], funcs: List[MethodTranslation]) + +final case class Translation(file: FileTranslation, programName: String) diff --git a/dotnet/src/main/scala/pravda/dotnet/translation/data/MethodTranslationCtx.scala b/dotnet/src/main/scala/pravda/dotnet/translation/data/MethodTranslationCtx.scala index 894685fb..ccfc2130 100644 --- a/dotnet/src/main/scala/pravda/dotnet/translation/data/MethodTranslationCtx.scala +++ b/dotnet/src/main/scala/pravda/dotnet/translation/data/MethodTranslationCtx.scala @@ -18,7 +18,7 @@ package pravda.dotnet.translation.data import pravda.dotnet.data.TablesData -import pravda.dotnet.data.TablesData.{MethodDebugInformationData, TypeDefData} +import pravda.dotnet.data.TablesData.{FieldData, MethodDebugInformationData, MethodDefData, TypeDefData} import pravda.dotnet.parser.CIL.CilData import pravda.dotnet.parser.Signatures @@ -27,44 +27,21 @@ final case class TranslationCtx( cilData: CilData, mainProgramClass: TypeDefData, programClasses: List[TypeDefData], - methodsToTypes: Map[Int, TypeDefData], + structs: List[TypeDefData], + methodIndex: TypeDefInvertedFileIndex[MethodDefData], + fieldIndex: TypeDefInvertedFileIndex[FieldData], pdbTables: Option[TablesData] ) { - def tpeByMethodDef(m: TablesData.MethodDefData): Option[TypeDefData] = { - val idx = cilData.tables.methodDefTable.indexWhere(_ == m) - if (idx == -1) { - None - } else { - methodsToTypes.get(idx) - } - } + def fieldParent(fileIdx: Int): Option[TypeDefData] = fieldIndex.parent(fileIdx) - def isMainProgramMethod(idx: Int): Boolean = - methodsToTypes.get(idx).contains(mainProgramClass) + def isMainProgramMethod(fileIdx: Int): Boolean = + methodIndex.parent(fileIdx).contains(mainProgramClass) - def isMainProgramMethod(m: TablesData.MethodDefData): Boolean = { - val idx = cilData.tables.methodDefTable.indexWhere(_ == m) - if (idx == -1) { - false - } else { - isMainProgramMethod(idx) - } - } + def isProgramMethod(fileIdx: Int): Boolean = + methodIndex.parent(fileIdx).exists(programClasses.contains) - def isProgramMethod(idx: Int): Boolean = - methodsToTypes.get(idx).exists(programClasses.contains) - - def isProgramMethod(m: TablesData.MethodDefData): Boolean = { - val idx = cilData.tables.methodDefTable.indexWhere(_ == m) - if (idx == -1) { - false - } else { - isProgramMethod(idx) - } - } - - def methodRow(idx: Int): TablesData.MethodDefData = cilData.tables.methodDefTable(idx) + def methodRow(fileIdx: Int): TablesData.MethodDefData = cilData.tables.methodDefTable(fileIdx) } final case class MethodTranslationCtx( diff --git a/dotnet/src/main/scala/pravda/dotnet/translation/data/TypeDefInvertedIndex.scala b/dotnet/src/main/scala/pravda/dotnet/translation/data/TypeDefInvertedIndex.scala new file mode 100644 index 00000000..68b436ce --- /dev/null +++ b/dotnet/src/main/scala/pravda/dotnet/translation/data/TypeDefInvertedIndex.scala @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2018 Expload.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package pravda.dotnet.translation.data + +import pravda.dotnet.data.TablesData.TypeDefData +import pravda.dotnet.parser.FileParser.ParsedDotnetFile +import pravda.dotnet.translation.NamesBuilder + +/** + * Inverted index for `TypeDef` component + * + * @param all all components from given `TypeDef`s + * @param fromNames a map from full name of component to the index in `all` + * @param parents a map from index in `all` to the parent `TypeDef` + */ +final case class TypeDefInvertedIndex[T](all: Vector[T], fromNames: Map[String, Int], parents: Map[Int, TypeDefData]) { + + def apply(i: Int): T = all(i) + + def parent(i: Int): Option[TypeDefData] = parents.get(i) + + def byName(name: String): Option[(TypeDefData, T)] = + for { + idx <- fromNames.get(name) + p <- parents.get(idx) + } yield (p, all(idx)) + + private[data] def merge(other: TypeDefInvertedIndex[T]): TypeDefInvertedIndex[T] = + new TypeDefInvertedIndex[T](all ++ other.all, fromNames ++ other.fromNames, parents ++ other.parents) +} + +object TypeDefInvertedIndex { + + /** + * Constructs inverted index from `TypeDef`s + * + * @param typeDefs + * @param fromTypeDef a function to retrieve components from `TypeDef` + * @param name a function to construct component specific prefix, + * this prefix is appended to full name of the parent `TypeDef` + * @return + */ + def apply[T](typeDefs: Seq[TypeDefData], + fromTypeDef: TypeDefData => Seq[T], + name: T => String): TypeDefInvertedIndex[T] = apply(typeDefs, fromTypeDef, name) + + private[data] def apply[T](typeDefs: Seq[TypeDefData], + fromTypeDef: TypeDefData => Seq[T], + name: T => String, + offset: Int): TypeDefInvertedIndex[T] = { + val withTd = typeDefs.flatMap { td => + fromTypeDef(td).map(t => t -> td) + } + + val (all, names, parents) = withTd.zipWithIndex.map { + case ((t, td), i) => + (t, s"${NamesBuilder.fullTypeDef(td)}.${name(t)}" -> (i + offset), (i + offset) -> td) + }.unzip3 + + new TypeDefInvertedIndex[T](all.toVector, names.toMap, parents.toMap) + } +} + +/** + * `TypeDefInvertedIndex` with additional indices indicating components only from one file + * + * @param index Global inverted index + * @param indexIdx Indices of components from `index.all` that belong to one file + */ +final case class TypeDefInvertedFileIndex[T](index: TypeDefInvertedIndex[T], indexIdx: Vector[Int]) { + + def apply(fileIdx: Int): T = index(indexIdx(fileIdx)) + + def parent(fileIdx: Int): Option[TypeDefData] = index.parents.get(indexIdx(fileIdx)) + + def byName(name: String): Option[(TypeDefData, T)] = index.byName(name) +} + +object TypeDefInvertedFileIndex { + + /** + * Constructs sequence of `TypeDefInvertedIndex` for each given file + * + * @param fromTypeDef a function to retrieve components from `TypeDef`, similar to function from `TypeDefInvertedIndex.apply` + * @param name a function to construct component specific prefix + * @return + */ + def apply[T](files: Seq[ParsedDotnetFile], + fromTypeDef: TypeDefData => Seq[T], + name: (ParsedDotnetFile, T) => String): Seq[TypeDefInvertedFileIndex[T]] = { + val (index, fileIndices, _) = files.foldLeft[(TypeDefInvertedIndex[T], List[Vector[Int]], Int)]( + (new TypeDefInvertedIndex[T](Vector.empty, Map.empty, Map.empty), List.empty, 0)) { + case ((acc, fileIdxes, total), f) => + val index = + TypeDefInvertedIndex[T](f.parsedPe.cilData.tables.typeDefTable, fromTypeDef, (t: T) => name(f, t), total) + (acc.merge(index), total.until(total + index.all.size).toVector :: fileIdxes, total + index.all.size) + } + + fileIndices.reverse.map(fi => new TypeDefInvertedFileIndex[T](index, fi)) + } +} diff --git a/dotnet/src/main/scala/pravda/dotnet/translation/opcode/ArgsLocalsTranslations.scala b/dotnet/src/main/scala/pravda/dotnet/translation/opcode/ArgsLocalsTranslations.scala index 9cfd4608..4e128749 100644 --- a/dotnet/src/main/scala/pravda/dotnet/translation/opcode/ArgsLocalsTranslations.scala +++ b/dotnet/src/main/scala/pravda/dotnet/translation/opcode/ArgsLocalsTranslations.scala @@ -64,7 +64,7 @@ case object ArgsLocalsTranslations extends OneToManyTranslator { def computeArgOffset(num: Int, stackOffset: Int): Int = (ctx.argsCount - num) + stackOffset + ctx.localsCount + 1 + - (if (!ctx.func) 1 else 0) + (if (!ctx.func) 1 else 0) + (if (ctx.static) -1 else 0) // for method there's name of the method def storeLocal(num: Int): Either[InternalError, List[asm.Operation]] = diff --git a/dotnet/src/main/scala/pravda/dotnet/translation/opcode/ArrayTranslation.scala b/dotnet/src/main/scala/pravda/dotnet/translation/opcode/ArrayTranslation.scala index 060e0f1f..5d9d4dcb 100644 --- a/dotnet/src/main/scala/pravda/dotnet/translation/opcode/ArrayTranslation.scala +++ b/dotnet/src/main/scala/pravda/dotnet/translation/opcode/ArrayTranslation.scala @@ -35,10 +35,11 @@ object ArrayTranslation extends OneToManyTranslatorOnlyAsm { op match { case NewArr(TypeRefData(6, typeName, namespaceName)) => val arrTypeF: PartialFunction[(String, String), Operation] = { - case ("System", "Byte") => pushType(Data.Type.Int8) + case ("System", "SByte") => pushType(Data.Type.Int8) case ("System", "Char") => pushType(Data.Type.Int16) + case ("System", "Int16") => pushType(Data.Type.Int16) case ("System", "Int32") => pushType(Data.Type.Int32) - case ("System", "UInt32") => pushType(Data.Type.Uint32) + case ("System", "Int64") => pushType(Data.Type.Int64) case ("System", "Double") => pushType(Data.Type.Number) case ("System", "String") => pushType(Data.Type.Utf8) } @@ -71,7 +72,7 @@ object ArrayInitializationTranslation extends OpcodeTranslatorOnlyAsm { OpcodeDetectors.IntLoad(arraySize), NewArr(TypeRefData(_, typeName, namespaceName)), Dup, - LdToken(FieldData(_, fieldName, tokenSignIdx)), + LdToken(FieldData(_, _, fieldName, tokenSignIdx)), Call(MemberRefData(TypeRefData(_, "RuntimeHelpers", "System.Runtime.CompilerServices"), "InitializeArray", _)) ) => def bytesRva = @@ -95,15 +96,15 @@ object ArrayInitializationTranslation extends OpcodeTranslatorOnlyAsm { } yield size def data(bytes: fastparse.byte.all.Bytes): Option[Data] = (namespaceName, typeName) match { - case ("System", "Byte") => Some(Data.Array.Int8Array(bytes.toArray.toBuffer)) + case ("System", "SByte") => Some(Data.Array.Int8Array(bytes.toArray.toBuffer)) case ("System", "Char") => Some(Data.Array.Int16Array(bytes.grouped(2).map(_.toShort(ordering = ByteOrdering.LittleEndian)).toBuffer)) + case ("System", "Int16") => + Some(Data.Array.Int16Array(bytes.grouped(2).map(_.toShort(ordering = ByteOrdering.LittleEndian)).toBuffer)) case ("System", "Int32") => Some(Data.Array.Int32Array(bytes.grouped(4).map(_.toInt(ordering = ByteOrdering.LittleEndian)).toBuffer)) - case ("System", "UInt32") => - Some( - Data.Array.Int32Array( - bytes.grouped(4).map(_.toInt(signed = false, ordering = ByteOrdering.LittleEndian)).toBuffer)) + case ("System", "Int64") => + Some(Data.Array.Int64Array(bytes.grouped(8).map(_.toLong(ordering = ByteOrdering.LittleEndian)).toBuffer)) case ("System", "Double") => Some(Data.Array.NumberArray(bytes.grouped(8).map(_.reverse.toByteBuffer.getDouble).toBuffer)) case _ => None diff --git a/dotnet/src/main/scala/pravda/dotnet/translation/opcode/BytesTranslation.scala b/dotnet/src/main/scala/pravda/dotnet/translation/opcode/BytesTranslation.scala index 3ed2e7c6..c5018ed3 100644 --- a/dotnet/src/main/scala/pravda/dotnet/translation/opcode/BytesTranslation.scala +++ b/dotnet/src/main/scala/pravda/dotnet/translation/opcode/BytesTranslation.scala @@ -55,7 +55,7 @@ object BytesTranslation extends OneToManyTranslator { // TODO good idea to make translator dependent `vm` // TODO instead of `vm-api`. Right(List(pushInt(0x04), Operation(Opcodes.SCALL))) - case Some(MethodRefDefSig(_, _, _, _, 0, _, List(Tpe(Arr(U1, ArrayShape(1, Nil, Nil)), false)))) => + case Some(MethodRefDefSig(_, _, _, _, 0, _, List(Tpe(Arr(I1, ArrayShape(1, Nil, Nil)), false)))) => Right(List(Operation.Call(Some("stdlib_array_to_bytes")))) case None => Left(UnknownOpcode) diff --git a/dotnet/src/main/scala/pravda/dotnet/translation/opcode/CallsTranslation.scala b/dotnet/src/main/scala/pravda/dotnet/translation/opcode/CallsTranslation.scala index c2e2c1a6..0936f574 100644 --- a/dotnet/src/main/scala/pravda/dotnet/translation/opcode/CallsTranslation.scala +++ b/dotnet/src/main/scala/pravda/dotnet/translation/opcode/CallsTranslation.scala @@ -30,40 +30,22 @@ import pravda.vm.{Data, Opcodes} case object CallsTranslation extends OneToManyTranslator { - private val mappingsMethods = Set("get", "getDefault", "exists", "put") + private val mappingsMethods = Set("get_Item", "GetOrDefault", "ContainsKey", "set_Item") def detectMapping(sig: Signature): Boolean = { sig match { case TypeSig(Tpe(Generic(TypeDetectors.Mapping(), _), _)) => true + case FieldSig(Generic(TypeDetectors.Mapping(), _)) => true case _ => false } } - def fullMethodName(name: String, sigO: Option[Signature]): String = { - val normalizedName = if (name == ".ctor" || name == ".cctor") name.drop(1) else name - val sigParams = sigO.collect { case m: MethodRefDefSig => m.params }.getOrElse(List.empty) - if (sigParams.nonEmpty) { - s"${normalizedName}_${sigParams.map(_.tpe.mkString).mkString("_")}" - } else { - normalizedName - } - } - - def fullTypeName(namespace: String, name: String): String = - if (namespace.nonEmpty) { - s"$namespace.$name" - } else { - name - } - - def fullTypeDefName(typeDefData: TypeDefData): String = - fullTypeName(typeDefData.namespace, typeDefData.name) - override def deltaOffsetOne(op: CIL.Op, ctx: MethodTranslationCtx): Either[InnerTranslationError, Int] = { - def callMethodDef(m: MethodDefData): Int = { - val void = MethodExtractors.isVoid(m, ctx.tctx.signatures) - val program = ctx.tctx.isMainProgramMethod(m) - -m.params.length + (if (void) 0 else 1) + (if (program || ctx.static) 0 else -1) + def callMethodDef(p: TypeDefData, m: MethodDefData, sigIdx: Long): Int = { + val void = ctx.tctx.signatures.get(sigIdx).exists(MethodExtractors.isVoid) + val static = MethodExtractors.isStatic(m) + val program = ctx.tctx.programClasses.contains(p) + -m.params.length + (if (void) 0 else 1) + (if (program || static) 0 else -1) } def callMethodRef(signatureIdx: Long): Int = { @@ -73,23 +55,34 @@ case object CallsTranslation extends OneToManyTranslator { -paramsLen - 1 + (if (void) 0 else 1) } - op match { - case Call(m: MethodDefData) => Right(callMethodDef(m)) - case CallVirt(m: MethodDefData) => Right(callMethodDef(m)) - case NewObj(m: MethodDefData) => Right(callMethodDef(m) + 2) + def refToDef(m: MemberRefData): Option[(TypeDefData, MethodDefData)] = m match { + case MemberRefData(TypeRefData(_, name, namespace), methodName, signatureIdx) => + val key = + s"${NamesBuilder.fullType(namespace, name)}.${NamesBuilder.fullMethod(methodName, ctx.tctx.signatures.get(signatureIdx))}" + ctx.tctx.methodIndex.byName(key) + case _ => None + } + op match { case CallVirt(m @ MemberRefData(TypeRefData(_, name, namespace), methodName, signatureIdx)) - if ctx.tctx.programClasses.exists(cls => fullTypeDefName(cls) == fullTypeName(namespace, name)) => + if ctx.tctx.programClasses.exists(cls => + NamesBuilder.fullTypeDef(cls) == NamesBuilder.fullType(namespace, name)) => Right(callMethodRef(signatureIdx)) case Call(MemberRefData(TypeRefData(_, "Object", "System"), ".ctor", _)) => if (ctx.struct.isDefined) Right(-1) else Right(0) + case Call(MemberRefData(TypeRefData(_, "Actions", "Expload.Pravda"), "Transfer", _)) => Right(-2) + case Call(MemberRefData(TypeRefData(_, "Actions", "Expload.Pravda"), "TransferFromProgram", _)) => Right(-2) case Call(MemberRefData(TypeRefData(_, "Info", "Expload.Pravda"), "Sender", _)) => Right(1) case Call(MemberRefData(TypeRefData(_, "Info", "Expload.Pravda"), "Balance", _)) => Right(0) case Call(MemberRefData(TypeRefData(_, "Info", "Expload.Pravda"), "ProgramAddress", _)) => Right(1) + case Call(MemberRefData(TypeRefData(_, "Info", "Expload.Pravda"), "Callers", _)) => Right(1) + case Call(MemberRefData(TypeRefData(_, "Info", "Expload.Pravda"), "Height", _)) => Right(1) + case Call(MemberRefData(TypeRefData(_, "Info", "Expload.Pravda"), "LastBlockHash", _)) => Right(1) case Call(MemberRefData(TypeRefData(_, "StdLib", "Expload.Pravda"), "Ripemd160", _)) => Right(0) case Call(MemberRefData(TypeRefData(_, "StdLib", "Expload.Pravda"), "ValidateEd25519Signature", _)) => Right(-2) case Call(MemberRefData(TypeRefData(_, "StdLib", "Expload.Pravda"), "HexToBytes", _)) => Right(0) + case Call(MemberRefData(TypeRefData(_, "StdLib", "Expload.Pravda"), "BytesToHex", _)) => Right(0) case Call(MemberRefData(TypeRefData(_, "Error", "Expload.Pravda"), "Throw", _)) => Right(-1) case Call(MethodSpecData(MemberRefData(TypeRefData(_, "ProgramHelper", "Expload.Pravda"), "Program", _), _)) => Right(0) @@ -113,6 +106,29 @@ case object CallsTranslation extends OneToManyTranslator { res.getOrElse(Left(InternalError("Invalid signatures"))) + case Call(m: MethodDefData) => + ctx.tctx.methodIndex + .parent(m.id) + .map(p => Right(callMethodDef(p, m, m.signatureIdx))) + .getOrElse(Left(UnknownOpcode)) + case CallVirt(m: MethodDefData) => + ctx.tctx.methodIndex + .parent(m.id) + .map(p => Right(callMethodDef(p, m, m.signatureIdx))) + .getOrElse(Left(UnknownOpcode)) + case NewObj(m: MethodDefData) => + ctx.tctx.methodIndex + .parent(m.id) + .map(p => Right(callMethodDef(p, m, m.signatureIdx) + 2)) + .getOrElse(Left(UnknownOpcode)) + + case Call(m: MemberRefData) => + refToDef(m).map { case (p, d) => Right(callMethodDef(p, d, m.signatureIdx)) }.getOrElse(Left(UnknownOpcode)) + case CallVirt(m: MemberRefData) => + refToDef(m).map { case (p, d) => Right(callMethodDef(p, d, m.signatureIdx)) }.getOrElse(Left(UnknownOpcode)) + case NewObj(m: MemberRefData) => + refToDef(m).map { case (p, d) => Right(callMethodDef(p, d, m.signatureIdx) + 2) }.getOrElse(Left(UnknownOpcode)) + case _ => Left(UnknownOpcode) } } @@ -121,8 +137,8 @@ case object CallsTranslation extends OneToManyTranslator { stackOffsetO: Option[Int], ctx: MethodTranslationCtx): Either[InnerTranslationError, List[Operation]] = { - def clearObject(m: MethodDefData): List[Operation] = { - val void = MethodExtractors.isVoid(m, ctx.tctx.signatures) + def clearObject(m: MethodDefData, sigIdx: Long): List[Operation] = { + val void = ctx.tctx.signatures.get(sigIdx).exists(MethodExtractors.isVoid) val static = MethodExtractors.isStatic(m) if (static) { List.empty @@ -133,78 +149,78 @@ case object CallsTranslation extends OneToManyTranslator { } } - def callFunc(m: MethodDefData): Either[InnerTranslationError, List[Operation]] = - if (ctx.tctx.isMainProgramMethod(m)) { + def callFunc(p: TypeDefData, m: MethodDefData, sigIdx: Long): Either[InnerTranslationError, List[Operation]] = { + if (ctx.tctx.mainProgramClass == p) { Right(List(Operation.Call(Some(s"func_${m.name}")))) } else { - val tpeO = ctx.tctx.tpeByMethodDef(m) - tpeO match { - case Some(tpe) => - Right( - Operation.Call( - Some( - s"func_${fullTypeDefName(tpe)}.${fullMethodName(m.name, ctx.tctx.signatures.get(m.signatureIdx))}" - )) +: clearObject(m) - ) - case None => - Left(UnknownOpcode) - } + Right( + Operation.Call( + Some( + s"func_${NamesBuilder.fullTypeDef(p)}.${NamesBuilder.fullMethod(m.name, ctx.tctx.signatures.get(sigIdx))}" + )) +: clearObject(m, sigIdx) + ) } + } - op match { - case Call(m: MethodDefData) => callFunc(m) - case CallVirt(m: MethodDefData) => - if (!ctx.tctx.isMainProgramMethod(m) && MethodExtractors.isVirtual(m)) { - val res = for { - sig <- ctx.tctx.signatures.get(m.signatureIdx).collect { - case m: MethodRefDefSig => m - } - } yield { - val paramsCnt = MethodExtractors.methodParamsCount(sig) - Right( - List( - Operation.Push(Data.Primitive.Int32(paramsCnt + 1)), - Operation(Opcodes.DUPN), - Operation.StructGet(Some(Data.Primitive.Utf8(fullMethodName(m.name, Some(sig))))), - Operation.Call(None), - ) ++ clearObject(m) - ) + def callVirt(p: TypeDefData, m: MethodDefData, sigIdx: Long): Either[InnerTranslationError, List[Operation]] = + if (p != ctx.tctx.mainProgramClass && MethodExtractors.isVirtual(m) && !MethodExtractors.isStatic(m)) { + val res = for { + sig <- ctx.tctx.signatures.get(sigIdx).collect { + case m: MethodRefDefSig => m } - - res.getOrElse(Left(InternalError("Wrong signatures"))) - } else { - callFunc(m) + } yield { + val paramsCnt = MethodExtractors.methodParamsCount(sig) + Right( + List( + Operation.Push(Data.Primitive.Int32(paramsCnt + 1)), + Operation(Opcodes.DUPN), + Operation.StructGet(Some(Data.Primitive.Utf8(NamesBuilder.fullMethod(m.name, Some(sig))))), + Operation.Call(None) + ) ++ clearObject(m, sigIdx) + ) } - case NewObj(m: MethodDefData) => - val tpeO = ctx.tctx.tpeByMethodDef(m) - tpeO match { - case Some(tpe) => - Right( - List( - Operation.New(Data.Struct.empty), - Operation.Call(Some(s"vtable_${fullTypeDefName(tpe)}")) - ) ++ - m.params.length - .to(1, -1) - .toList - .flatMap( - i => - List( - Operation.Push(Data.Primitive.Int32(i + 1)), - Operation(Opcodes.SWAPN) - ) - ) // move object to 0-th arg - :+ Operation.Call( - Some( - s"func_${fullTypeDefName(tpe)}.${fullMethodName(m.name, ctx.tctx.signatures.get(m.signatureIdx))}" - ) - ) + + res.getOrElse(Left(InternalError("Wrong signatures"))) + } else { + callFunc(p, m, sigIdx) + } + + def newObj(p: TypeDefData, m: MethodDefData, sigIdx: Long): Either[InnerTranslationError, List[Operation]] = + Right( + List( + Operation.New(Data.Struct.empty), + Operation.Call(Some(s"vtable_${NamesBuilder.fullTypeDef(p)}")), + Operation.Call(Some(s"default_fields_${NamesBuilder.fullTypeDef(p)}")) + ) ++ + m.params.length + .to(1, -1) + .toList + .flatMap( + i => + List( + Operation.Push(Data.Primitive.Int32(i + 1)), + Operation(Opcodes.SWAPN) + ) + ) // move object to 0-th arg + :+ Operation.Call( + Some( + s"func_${NamesBuilder.fullTypeDef(p)}.${NamesBuilder.fullMethod(m.name, ctx.tctx.signatures.get(sigIdx))}" ) + ) + ) - case None => Left(UnknownOpcode) - } + def refToDef(m: MemberRefData): Option[(TypeDefData, MethodDefData)] = m match { + case MemberRefData(TypeRefData(_, name, namespace), methodName, signatureIdx) => + val key = + s"${NamesBuilder.fullType(namespace, name)}.${NamesBuilder.fullMethod(methodName, ctx.tctx.signatures.get(signatureIdx))}" + ctx.tctx.methodIndex.byName(key) + case _ => None + } + + op match { case CallVirt(m @ MemberRefData(TypeRefData(_, name, namespace), methodName, _)) - if ctx.tctx.programClasses.exists(cls => fullTypeDefName(cls) == fullTypeName(namespace, name)) => + if ctx.tctx.programClasses.exists(cls => + NamesBuilder.fullTypeDef(cls) == NamesBuilder.fullType(namespace, name)) => val paramsLen = MethodExtractors.methodParamsCount(m, ctx.tctx.signatures) val swapAddress = (2 to (paramsLen + 1)) .flatMap(i => List(Operation.Push(Data.Primitive.Int32(i)), Operation(Opcodes.SWAPN))) @@ -214,18 +230,31 @@ case object CallsTranslation extends OneToManyTranslator { Operation(Opcodes.SWAP), Operation.Push(Data.Primitive.Int32(paramsLen + 1)), Operation(Opcodes.PCALL))) + case Call(MemberRefData(TypeRefData(_, "Object", "System"), ".ctor", _)) => if (ctx.struct.isDefined) Right(List(Operation(Opcodes.POP))) else Right(List.empty) + case Call(MemberRefData(TypeRefData(_, "Actions", "Expload.Pravda"), "Transfer", _)) => + Right(List(Operation(Opcodes.TRANSFER))) + case Call(MemberRefData(TypeRefData(_, "Actions", "Expload.Pravda"), "TransferFromProgram", _)) => + Right(List(Operation(Opcodes.PTRANSFER))) case Call(MemberRefData(TypeRefData(_, "Info", "Expload.Pravda"), "Sender", _)) => Right(List(Operation(Opcodes.FROM))) case Call(MemberRefData(TypeRefData(_, "Info", "Expload.Pravda"), "Balance", _)) => Right(List(Operation(Opcodes.BALANCE))) case Call(MemberRefData(TypeRefData(_, "Info", "Expload.Pravda"), "ProgramAddress", _)) => Right(List(Operation(Opcodes.PADDR))) + case Call(MemberRefData(TypeRefData(_, "Info", "Expload.Pravda"), "Callers", _)) => + Right(List(Operation(Opcodes.CALLERS))) + case Call(MemberRefData(TypeRefData(_, "Info", "Expload.Pravda"), "Height", _)) => + Right(List(Operation(Opcodes.HEIGHT))) + case Call(MemberRefData(TypeRefData(_, "Info", "Expload.Pravda"), "LastBlockHash", _)) => + Right(List(Operation(Opcodes.HASH))) case Call(MemberRefData(TypeRefData(_, "StdLib", "Expload.Pravda"), "Ripemd160", _)) => Right(List(Operation.Push(Data.Primitive.Int32(2)), Operation(Opcodes.SCALL))) case Call(MemberRefData(TypeRefData(_, "StdLib", "Expload.Pravda"), "HexToBytes", _)) => Right(List(Operation.Push(Data.Primitive.Int32(4)), Operation(Opcodes.SCALL))) + case Call(MemberRefData(TypeRefData(_, "StdLib", "Expload.Pravda"), "BytesToHex", _)) => + Right(List(Operation.Push(Data.Primitive.Int32(5)), Operation(Opcodes.SCALL))) case Call(MemberRefData(TypeRefData(_, "StdLib", "Expload.Pravda"), "ValidateEd25519Signature", _)) => Right(List(Operation.Push(Data.Primitive.Int32(1)), Operation(Opcodes.SCALL))) case Call(MemberRefData(TypeRefData(_, "Error", "Expload.Pravda"), "Throw", _)) => @@ -240,19 +269,19 @@ case object CallsTranslation extends OneToManyTranslator { } yield { if (detectMapping(parentSig)) { name match { - case "get" => + case "get_Item" => Right( cast(Data.Type.Bytes) ++ List(Operation(Opcodes.SWAP), Operation(Opcodes.CONCAT), Operation(Opcodes.SGET))) - case "getDefault" => Right(List(Operation.Call(Some("stdlib_storage_get_default")))) - case "exists" => + case "GetOrDefault" => Right(List(Operation.Call(Some("stdlib_storage_get_default")))) + case "ContainsKey" => Right( cast(Data.Type.Bytes) ++ List(Operation(Opcodes.SWAP), Operation(Opcodes.CONCAT), Operation(Opcodes.SEXIST)) ++ cast(Data.Type.Int32)) - case "put" => + case "set_Item" => Right( dupn(2) ++ cast(Data.Type.Bytes) ++ dupn(4) ++ List( @@ -271,6 +300,29 @@ case object CallsTranslation extends OneToManyTranslator { res.getOrElse(Left(InternalError("Invalid signatures"))) + case Call(m: MethodDefData) => + ctx.tctx.methodIndex + .parent(m.id) + .map(p => callFunc(p, m, m.signatureIdx)) + .getOrElse(Left(UnknownOpcode)) + case CallVirt(m: MethodDefData) => + ctx.tctx.methodIndex + .parent(m.id) + .map(p => callVirt(p, m, m.signatureIdx)) + .getOrElse(Left(UnknownOpcode)) + case NewObj(m: MethodDefData) => + ctx.tctx.methodIndex + .parent(m.id) + .map(p => newObj(p, m, m.signatureIdx)) + .getOrElse(Left(UnknownOpcode)) + + case Call(m: MemberRefData) => + refToDef(m).map { case (p, d) => callFunc(p, d, m.signatureIdx) }.getOrElse(Left(UnknownOpcode)) + case CallVirt(m: MemberRefData) => + refToDef(m).map { case (p, d) => callVirt(p, d, m.signatureIdx) }.getOrElse(Left(UnknownOpcode)) + case NewObj(m: MemberRefData) => + refToDef(m).map { case (p, d) => newObj(p, d, m.signatureIdx) }.getOrElse(Left(UnknownOpcode)) + case _ => Left(UnknownOpcode) } } diff --git a/dotnet/src/main/scala/pravda/dotnet/translation/opcode/ConvertTranslation.scala b/dotnet/src/main/scala/pravda/dotnet/translation/opcode/ConvertTranslation.scala index 18f2f087..7660b252 100644 --- a/dotnet/src/main/scala/pravda/dotnet/translation/opcode/ConvertTranslation.scala +++ b/dotnet/src/main/scala/pravda/dotnet/translation/opcode/ConvertTranslation.scala @@ -34,7 +34,7 @@ object ConvertTranslation extends OneToManyTranslatorOnlyAsm { case Call(MemberRefData(TypeRefData(_, "Convert", "System"), "ToInt16", _)) => Right(cast(Data.Type.Int16)) case Call(MemberRefData(TypeRefData(_, "Convert", "System"), "ToInt32", _)) => Right(cast(Data.Type.Int32)) case Call(MemberRefData(TypeRefData(_, "Convert", "System"), "ToString", _)) => Right(cast(Data.Type.Utf8)) - case Call(MemberRefData(TypeRefData(_, "Convert", "System"), "ToByte", _)) => Right(cast(Data.Type.Int8)) + case Call(MemberRefData(TypeRefData(_, "Convert", "System"), "ToSByte", _)) => Right(cast(Data.Type.Int8)) case _ => Left(UnknownOpcode) } } diff --git a/dotnet/src/main/scala/pravda/dotnet/translation/opcode/FieldsTranslation.scala b/dotnet/src/main/scala/pravda/dotnet/translation/opcode/FieldsTranslation.scala index d5c2f877..19ddb4b6 100644 --- a/dotnet/src/main/scala/pravda/dotnet/translation/opcode/FieldsTranslation.scala +++ b/dotnet/src/main/scala/pravda/dotnet/translation/opcode/FieldsTranslation.scala @@ -23,87 +23,126 @@ import pravda.dotnet.data.TablesData._ import pravda.dotnet.parser.CIL import pravda.dotnet.parser.CIL._ import pravda.dotnet.parser.Signatures._ -import pravda.dotnet.translation.TypeDetectors +import pravda.dotnet.translation.{FieldExtractors, NamesBuilder, TypeDetectors} import pravda.dotnet.translation.data._ import pravda.vm.{Data, Opcodes, asm} case object FieldsTranslation extends OneToManyTranslatorOnlyAsm { - def isStatic(flags: Short): Boolean = (flags & 0x10) != 0 - override def asmOpsOne(op: CIL.Op, stackOffsetO: Option[Int], ctx: MethodTranslationCtx): Either[InnerTranslationError, List[asm.Operation]] = { - def loadField(name: String, sigIdx: Long, static: Boolean): List[asm.Operation] = { - lazy val defaultLoadForProgram = List( - pushString(s"p_$name"), - asm.Operation(Opcodes.SGET) - ) + def refToDef(m: MemberRefData): Option[(TypeDefData, FieldData)] = m match { + case MemberRefData(TypeRefData(_, name, namespace), fieldName, signatureIdx) => + val key = s"${NamesBuilder.fullType(namespace, name)}.$fieldName" + ctx.tctx.fieldIndex.byName(key) + case _ => None + } + + def loadField(p: TypeDefData, fd: FieldData, sigIdx: Long): Either[InnerTranslationError, List[asm.Operation]] = { + lazy val defaultLoad = Right( + List( + pushString(s"p_${fd.name}"), + asm.Operation(Opcodes.SGET) + )) - ctx.struct match { - case Some(structName) => - if (static) { + if (p == ctx.tctx.mainProgramClass) { + ctx.tctx.signatures.get(sigIdx) match { + case Some(FieldSig(tpe)) => + tpe match { + case SigType.Generic(TypeDetectors.Mapping(), _) => + Right(List(pushBytes(fd.name.getBytes(StandardCharsets.UTF_8)))) + case _ => + defaultLoad + } + case _ => defaultLoad + } + } else if (ctx.tctx.programClasses.contains(p)) { + Left(InternalError("Reading fields from other [Program] classes is forbidden")) + } else { + if (FieldExtractors.isStatic(fd.flags)) { + Right( List( - pushString(s"s_${structName}_$name"), + pushString(s"s_${NamesBuilder.fullTypeDef(p)}_${fd.name}"), asm.Operation(Opcodes.SGET) - ) - } else { - List(asm.Operation.StructGet(Some(Data.Primitive.Utf8(name)))) - } - case None => - ctx.tctx.signatures.get(sigIdx) match { - case Some(FieldSig(tpe)) => - tpe match { - case SigType.Generic(TypeDetectors.Mapping(), _) => - List(pushBytes(name.getBytes(StandardCharsets.UTF_8))) - case _ => defaultLoadForProgram - } - case _ => defaultLoadForProgram - } + )) + } else { + Right(List(asm.Operation.StructGet(Some(Data.Primitive.Utf8(fd.name))))) + } } } - def storeField(name: String, sigIdx: Long, static: Boolean): List[asm.Operation] = { - lazy val defaultStore = List( - pushString(s"p_$name"), - asm.Operation(Opcodes.SPUT) - ) + def storeField(p: TypeDefData, fd: FieldData, sigIdx: Long): Either[InnerTranslationError, List[asm.Operation]] = { + lazy val defaultStore = Right( + List( + pushString(s"p_${fd.name}"), + asm.Operation(Opcodes.SPUT) + )) - ctx.struct match { - case Some(structName) => - if (static) { - List( - pushString(s"s_${structName}_$name"), - asm.Operation(Opcodes.SPUT) - ) - } else { - List(asm.Operation.StructMut(Some(Data.Primitive.Utf8(name)))) - } - case None => - ctx.tctx.signatures.get(sigIdx) match { - case Some(FieldSig(tpe)) => - tpe match { - case SigType.Generic(TypeDetectors.Mapping(), _) => + if (p == ctx.tctx.mainProgramClass) { + ctx.tctx.signatures.get(sigIdx) match { + case Some(FieldSig(tpe)) => + tpe match { + case SigType.Generic(TypeDetectors.Mapping(), _) => + Right( List( asm.Operation(Opcodes.POP), asm.Operation(Opcodes.POP), pushString("Mapping modification is forbidden"), asm.Operation(Opcodes.THROW) - ) - case _ => defaultStore - } - case _ => defaultStore - } + )) + case _ => defaultStore + } + case _ => defaultStore + } + } else if (ctx.tctx.programClasses.contains(p)) { + Left(InternalError("Writing fields from other [Program] classes is forbidden")) + } else { + if (FieldExtractors.isStatic(fd.flags)) { + Right( + List( + pushString(s"s_${NamesBuilder.fullTypeDef(p)}_${fd.name}"), + asm.Operation(Opcodes.SPUT) + )) + } else { + Right(List(asm.Operation.StructMut(Some(Data.Primitive.Utf8(fd.name))))) + } } } op match { - case LdSFld(FieldData(flags, name, sig)) => Right(loadField(name, sig, isStatic(flags))) - case LdFld(FieldData(flags, name, sig)) => Right(loadField(name, sig, isStatic(flags))) - case StSFld(FieldData(flags, name, sig)) => Right(storeField(name, sig, isStatic(flags))) - case StFld(FieldData(flags, name, sig)) => Right(storeField(name, sig, isStatic(flags))) - case _ => Left(UnknownOpcode) + case LdSFld(fd: FieldData) => + ctx.tctx.fieldIndex + .parent(fd.id) + .map(p => loadField(p, fd, fd.signatureIdx)) + .getOrElse(Left(UnknownOpcode)) + case LdFld(fd: FieldData) => + ctx.tctx.fieldIndex + .parent(fd.id) + .map(p => loadField(p, fd, fd.signatureIdx)) + .getOrElse(Left(UnknownOpcode)) + case StSFld(fd: FieldData) => + ctx.tctx.fieldIndex + .parent(fd.id) + .map(p => storeField(p, fd, fd.signatureIdx)) + .getOrElse(Left(UnknownOpcode)) + case StFld(fd: FieldData) => + ctx.tctx.fieldIndex + .parent(fd.id) + .map(p => storeField(p, fd, fd.signatureIdx)) + .getOrElse(Left(UnknownOpcode)) + + case LdSFld(m: MemberRefData) => + refToDef(m).map { case (p, fd) => loadField(p, fd, m.signatureIdx) }.getOrElse(Left(UnknownOpcode)) + case LdFld(m: MemberRefData) => + refToDef(m).map { case (p, fd) => loadField(p, fd, m.signatureIdx) }.getOrElse(Left(UnknownOpcode)) + case StSFld(m: MemberRefData) => + refToDef(m).map { case (p, fd) => storeField(p, fd, m.signatureIdx) }.getOrElse(Left(UnknownOpcode)) + case StFld(m: MemberRefData) => + refToDef(m).map { case (p, fd) => storeField(p, fd, m.signatureIdx) }.getOrElse(Left(UnknownOpcode)) + + case _ => Left(UnknownOpcode) } } } diff --git a/dotnet/src/main/scala/pravda/dotnet/translation/opcode/JumpsTranslation.scala b/dotnet/src/main/scala/pravda/dotnet/translation/opcode/JumpsTranslation.scala index c3a809b0..a63050d1 100644 --- a/dotnet/src/main/scala/pravda/dotnet/translation/opcode/JumpsTranslation.scala +++ b/dotnet/src/main/scala/pravda/dotnet/translation/opcode/JumpsTranslation.scala @@ -33,7 +33,7 @@ case object JumpsTranslation extends OneToManyTranslatorOnlyAsm { op match { case Jump(label) => Right(List(asm.Operation.Jump(Some(label)))) case JumpI(label) => - Right(List(pushInt(1), asm.Operation(Opcodes.EQ), asm.Operation.JumpI(Some(label)))) + Right(List(pushInt(0), asm.Operation(Opcodes.EQ), asm.Operation(Opcodes.NOT), asm.Operation.JumpI(Some(label)))) case Label(label) => Right(List(asm.Operation.Label(label))) case _ => Left(UnknownOpcode) } diff --git a/dotnet/src/main/scala/pravda/dotnet/translation/opcode/MappingTranslation.scala b/dotnet/src/main/scala/pravda/dotnet/translation/opcode/MappingInitializationTranslation.scala similarity index 97% rename from dotnet/src/main/scala/pravda/dotnet/translation/opcode/MappingTranslation.scala rename to dotnet/src/main/scala/pravda/dotnet/translation/opcode/MappingInitializationTranslation.scala index 558d4dd6..a73e87e7 100644 --- a/dotnet/src/main/scala/pravda/dotnet/translation/opcode/MappingTranslation.scala +++ b/dotnet/src/main/scala/pravda/dotnet/translation/opcode/MappingInitializationTranslation.scala @@ -28,7 +28,7 @@ object MappingInitializationTranslation extends OpcodeTranslatorOnlyAsm { stackOffsetO: Option[Int], ctx: MethodTranslationCtx): Either[InnerTranslationError, (Taken, List[Operation])] = ops.take(2) match { - case List(NewObj(MemberRefData(TypeSpecData(signIdx), ".ctor", _)), StFld(FieldData(_, name, _))) => + case List(NewObj(MemberRefData(TypeSpecData(signIdx), ".ctor", _)), StFld(FieldData(_, _, name, _))) => val res = for { parentSig <- ctx.tctx.signatures.get(signIdx) } yield { diff --git a/dotnet/src/main/scala/pravda/dotnet/translation/opcode/SimpleTranslations.scala b/dotnet/src/main/scala/pravda/dotnet/translation/opcode/SimpleTranslations.scala index c2ce390d..dd613a9e 100644 --- a/dotnet/src/main/scala/pravda/dotnet/translation/opcode/SimpleTranslations.scala +++ b/dotnet/src/main/scala/pravda/dotnet/translation/opcode/SimpleTranslations.scala @@ -30,21 +30,24 @@ case object SimpleTranslations extends OneToManyTranslatorOnlyAsm { ctx: MethodTranslationCtx): Either[InnerTranslationError, List[Operation]] = { val translateF: PartialFunction[CIL.Op, List[Operation]] = { - case LdcI40 => List(pushInt(0)) - case LdcI41 => List(pushInt(1)) - case LdcI42 => List(pushInt(2)) - case LdcI43 => List(pushInt(3)) - case LdcI44 => List(pushInt(4)) - case LdcI45 => List(pushInt(5)) - case LdcI46 => List(pushInt(6)) - case LdcI47 => List(pushInt(7)) - case LdcI48 => List(pushInt(8)) - case LdcI4M1 => List(pushInt(-1)) + case LdcI40 => List(pushInt(0)) + case LdcI41 => List(pushInt(1)) + case LdcI42 => List(pushInt(2)) + case LdcI43 => List(pushInt(3)) + case LdcI44 => List(pushInt(4)) + case LdcI45 => List(pushInt(5)) + case LdcI46 => List(pushInt(6)) + case LdcI47 => List(pushInt(7)) + case LdcI48 => List(pushInt(8)) + case LdcI4M1 => List(pushInt(-1)) + case LdcI4(num) => List(pushInt(num)) case LdcI4S(v) => List(pushInt(v.toInt)) + case LdcI8(v) => List(pushBigInt(v)) case LdcR4(f) => List(pushFloat(f.toDouble)) case LdcR8(d) => List(pushFloat(d)) case LdStr(s) => List(Operation.Push(Data.Primitive.Utf8(s))) + case LdNull => List(Operation.Push(Data.Primitive.Null)) case ConvI1 => cast(Data.Type.Int8) case ConvU1 => cast(Data.Type.Int8) @@ -52,15 +55,22 @@ case object SimpleTranslations extends OneToManyTranslatorOnlyAsm { case ConvU2 => cast(Data.Type.Int16) case ConvI4 => cast(Data.Type.Int32) case ConvU4 => cast(Data.Type.Int32) - case ConvI8 => cast(Data.Type.BigInt) + case ConvI8 => cast(Data.Type.Int64) case ConvU8 => cast(Data.Type.BigInt) + case ConvR4 => cast(Data.Type.Number) + case ConvR8 => cast(Data.Type.Number) + case Add => List(Operation(Opcodes.ADD)) case Mul => List(Operation(Opcodes.MUL)) case Div => List(Operation(Opcodes.SWAP), Operation(Opcodes.DIV)) case Rem => List(Operation(Opcodes.SWAP), Operation(Opcodes.MOD)) case Sub => List(pushInt(-1), Operation(Opcodes.MUL), Operation(Opcodes.ADD)) + case Or => List(Operation(Opcodes.OR)) + case And => List(Operation(Opcodes.AND)) + case Xor => List(Operation(Opcodes.XOR)) + case Clt => Operation(Opcodes.SWAP) :: Operation(Opcodes.LT) :: cast(Data.Type.Int32) case CltUn => Operation(Opcodes.SWAP) :: Operation(Opcodes.LT) :: cast(Data.Type.Int32) case Cgt => Operation(Opcodes.SWAP) :: Operation(Opcodes.GT) :: cast(Data.Type.Int32) @@ -69,8 +79,9 @@ case object SimpleTranslations extends OneToManyTranslatorOnlyAsm { case Not => cast(Data.Type.Boolean) ++ (Operation(Opcodes.NOT) :: cast(Data.Type.Int32)) - case Dup => List(Operation.Orphan(Opcodes.DUP)) + case Dup => List(Operation(Opcodes.DUP)) + case Pop => List(Operation(Opcodes.POP)) case Nop => List() case Ret => List(Operation.Jump(Some(s"${ctx.name}_lvc"))) } diff --git a/dotnet/src/main/scala/pravda/dotnet/translation/opcode/StdlibAsm.scala b/dotnet/src/main/scala/pravda/dotnet/translation/opcode/StdlibAsm.scala index cd6e4aec..74631e5f 100644 --- a/dotnet/src/main/scala/pravda/dotnet/translation/opcode/StdlibAsm.scala +++ b/dotnet/src/main/scala/pravda/dotnet/translation/opcode/StdlibAsm.scala @@ -97,6 +97,40 @@ object StdlibAsm { Operation(Opcodes.POP), Operation(Opcodes.RET) ) + ), + stdlibFunc( + "concat_all_string", + List( + Operation(Opcodes.DUP), + Operation(Opcodes.LENGTH), + Operation.Push(Data.Primitive.Utf8("")), + pushInt(0), + Operation.Label("concat_all_string_loop"), + pushInt(4), + Operation(Opcodes.DUPN), + pushInt(2), + Operation(Opcodes.DUPN), + Operation(Opcodes.ARRAY_GET), + pushInt(3), + Operation(Opcodes.DUPN), + Operation(Opcodes.CONCAT), + pushInt(3), + Operation(Opcodes.SWAPN), + Operation(Opcodes.POP), + pushInt(1), + Operation(Opcodes.ADD), + Operation(Opcodes.DUP), + pushInt(4), + Operation(Opcodes.DUPN), + Operation(Opcodes.GT), + Operation.JumpI(Some("concat_all_string_loop")), + Operation(Opcodes.POP), + Operation(Opcodes.SWAP), + Operation(Opcodes.POP), + Operation(Opcodes.SWAP), + Operation(Opcodes.POP), + Operation(Opcodes.RET) + ) ) ) } diff --git a/dotnet/src/main/scala/pravda/dotnet/translation/opcode/StringTranslation.scala b/dotnet/src/main/scala/pravda/dotnet/translation/opcode/StringTranslation.scala index 64576b94..fe950526 100644 --- a/dotnet/src/main/scala/pravda/dotnet/translation/opcode/StringTranslation.scala +++ b/dotnet/src/main/scala/pravda/dotnet/translation/opcode/StringTranslation.scala @@ -17,10 +17,10 @@ package pravda.dotnet.translation.opcode import pravda.dotnet.data.TablesData.{MemberRefData, TypeRefData} -import pravda.dotnet.parser.CIL +import pravda.dotnet.parser.{CIL, Signatures} import pravda.dotnet.parser.CIL._ -import pravda.dotnet.parser.Signatures.MethodRefDefSig -import pravda.dotnet.translation.data.{InternalError, MethodTranslationCtx, InnerTranslationError, UnknownOpcode} +import pravda.dotnet.parser.Signatures.{MethodRefDefSig, SigType} +import pravda.dotnet.translation.data.{InnerTranslationError, InternalError, MethodTranslationCtx, UnknownOpcode} import pravda.vm.{Data, Opcodes} import pravda.vm.asm.Operation @@ -34,17 +34,17 @@ case object StringTranslation extends OneToManyTranslatorOnlyAsm { case LdStr(s) => Right(List(Operation.Push(Data.Primitive.Utf8(s)))) case Call(MemberRefData(TypeRefData(_, "String", "System"), "Concat", signatureIdx)) => - val strsCntE = (for { + (for { sign <- ctx.tctx.signatures.get(signatureIdx) } yield sign match { - case MethodRefDefSig(_, _, _, _, _, _, params) => Right(params.length) - case _ => Left(UnknownOpcode) + case MethodRefDefSig(_, _, _, _, _, _, params) if params.forall(_.tpe == SigType.String) => + Right(List.fill(params.length - 1)(List(Operation(Opcodes.SWAP), Operation(Opcodes.CONCAT))).flatten) + case MethodRefDefSig(_, _, _, _, _, _, List(Signatures.Tpe(SigType.Arr(SigType.String, _), _))) => + Right(List(Operation.Call(Some("stdlib_concat_all_string")))) + case _ => + Left(UnknownOpcode) }).getOrElse(Left(InternalError("Invalid signatures"))) - - for { - strsCnt <- strsCntE - } yield List.fill(strsCnt - 1)(List(Operation(Opcodes.SWAP), Operation(Opcodes.CONCAT))).flatten case CallVirt(MemberRefData(TypeRefData(_, "String", "System"), "get_Chars", _)) => Right(List(Operation(Opcodes.ARRAY_GET))) case CallVirt(MemberRefData(TypeRefData(_, "String", "System"), "Substring", signatureIdx)) => // FIXME more accurate method detection diff --git a/dotnet/src/main/scala/pravda/dotnet/translation/opcode/package.scala b/dotnet/src/main/scala/pravda/dotnet/translation/opcode/package.scala index e290b970..3c767b6b 100644 --- a/dotnet/src/main/scala/pravda/dotnet/translation/opcode/package.scala +++ b/dotnet/src/main/scala/pravda/dotnet/translation/opcode/package.scala @@ -37,6 +37,9 @@ package object opcode { def pushBytes(d: Array[Byte]): asm.Operation = push(d, (d: Array[Byte]) => Data.Primitive.Bytes(ByteString.copyFrom(d))) + def pushBigInt(l: Long): asm.Operation = + push(l, (l: Long) => Data.Primitive.BigInt(BigInt(l))) + def pushType(tpe: Data.Type): asm.Operation = push(tpe, Data.Primitive.Int8) diff --git a/dotnet/src/main/scala/pravda/dotnet/translation/package.scala b/dotnet/src/main/scala/pravda/dotnet/translation/package.scala index c265e2f4..ecb2840e 100644 --- a/dotnet/src/main/scala/pravda/dotnet/translation/package.scala +++ b/dotnet/src/main/scala/pravda/dotnet/translation/package.scala @@ -119,4 +119,39 @@ package object translation { def isVirtual(m: MethodDefData): Boolean = (m.flags & 0x40) != 0 } + + object FieldExtractors { + + def isPrivate(f: FieldData): Boolean = + (f.flags & 0x7) == 0x1 + + def isStatic(flags: Short): Boolean = + (flags & 0x10) != 0 + } + + object NamesBuilder { + + def fullMethod(name: String, sigO: Option[Signature]): String = { + val normalizedName = if (name == ".ctor" || name == ".cctor") name.drop(1) else name + val sigParams = sigO.collect { case m: MethodRefDefSig => m.params }.getOrElse(List.empty) + if (sigParams.nonEmpty) { + s"${normalizedName}_${sigParams.map(_.tpe.mkString).mkString("_")}" + } else { + normalizedName + } + } + + def fullType(namespace: String, name: String): String = + if (namespace.nonEmpty) { + s"$namespace.$name" + } else { + name + } + + def fullTypeDef(typeDefData: TypeDefData): String = + fullType(typeDefData.namespace, typeDefData.name) + + def fullTypeMethod(typeDef: TypeDefData, methodName: String, sig: Option[Signature]): String = + s"${fullTypeDef(typeDef)}.${fullMethod(methodName, sig)}" + } } diff --git a/dotnet/src/test/resources/parser/Arithmetics.prs b/dotnet/src/test/resources/parser/Arithmetics.prs new file mode 100644 index 00000000..a88e8e84 --- /dev/null +++ b/dotnet/src/test/resources/parser/Arithmetics.prs @@ -0,0 +1,85 @@ +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: Arithmetics.exe + sources: + - Pravda.dll + - dotnet-tests/resources/Arithmetics.cs + optimize: true +--- +methods: |- + List( + Method( + List( + LdArg0, + LdFld(FieldData(0, 1, "X", 31L)), + LdcI42, + Add, + LdArg0, + LdFld(FieldData(0, 1, "X", 31L)), + LdcI42, + Mul, + StLoc0, + LdArg0, + LdFld(FieldData(0, 1, "X", 31L)), + LdcI42, + Div, + StLoc1, + LdArg0, + LdFld(FieldData(0, 1, "X", 31L)), + LdcI42, + Rem, + StLoc2, + LdLoc0, + Add, + LdcI4S(42), + Add, + LdLoc1, + Mul, + LdLoc2, + Add, + LdcI4(1337), + Div, + Ret + ), + 3, + Some(16L) + ), + Method(List(Ret), 0, None), + Method( + List( + LdArg0, + LdcI4S(10), + StFld(FieldData(0, 1, "X", 31L)), + LdArg0, + Call(MemberRefData(TypeRefData(6L, "Object", "System"), ".ctor", 6L)), + Ret + ), + 0, + None + ) + ) +signatures: |- + List( + (1L, MethodRefDefSig(true, false, false, false, 0, Tpe(Void, false), List(Tpe(I4, false)))), + (6L, MethodRefDefSig(true, false, false, false, 0, Tpe(Void, false), List())), + ( + 10L, + MethodRefDefSig( + true, + false, + false, + false, + 0, + Tpe(Void, false), + List(Tpe(ValueTpe(TypeRefData(15L, "DebuggingModes", "")), false)) + ) + ), + (16L, LocalVarSig(List(LocalVar(I4, false), LocalVar(I4, false), LocalVar(I4, false)))), + (31L, FieldSig(I4)), + (34L, MethodRefDefSig(true, false, false, false, 0, Tpe(I4, false), List())), + (38L, MethodRefDefSig(false, false, false, false, 0, Tpe(Void, false), List())) + ) diff --git a/dotnet/src/test/resources/parser/arrays.prs b/dotnet/src/test/resources/parser/Arrays.prs similarity index 71% rename from dotnet/src/test/resources/parser/arrays.prs rename to dotnet/src/test/resources/parser/Arrays.prs index aad20419..da872b2e 100644 --- a/dotnet/src/test/resources/parser/arrays.prs +++ b/dotnet/src/test/resources/parser/Arrays.prs @@ -1,112 +1,115 @@ -exe: arrays.exe +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: Arrays.exe + sources: + - Pravda.dll + - dotnet-tests/resources/Arrays.cs + optimize: true --- methods: |- List( Method( List( - Nop, LdcI43, - NewArr(TypeRefData(6L, "Byte", "System")), + NewArr(TypeRefData(6L, "SByte", "System")), Dup, - LdToken(FieldData(307, "7037807198C22A7D2B0807371D763779A84FDFCF", 119L)), + LdToken(FieldData(4, 307, "7037807198C22A7D2B0807371D763779A84FDFCF", 114L)), Call( MemberRefData( TypeRefData(6L, "RuntimeHelpers", "System.Runtime.CompilerServices"), "InitializeArray", - 33L + 25L ) ), StLoc0, LdcI43, - NewArr(TypeRefData(6L, "Byte", "System")), + NewArr(TypeRefData(6L, "SByte", "System")), Dup, - LdToken(FieldData(307, "E809C5D1CEA47B45E34701D23F608A9A58034DC9", 119L)), + LdToken(FieldData(8, 307, "E809C5D1CEA47B45E34701D23F608A9A58034DC9", 114L)), Call( MemberRefData( TypeRefData(6L, "RuntimeHelpers", "System.Runtime.CompilerServices"), "InitializeArray", - 33L + 25L ) ), - NewObj(MemberRefData(TypeRefData(10L, "Bytes", "Expload.Pravda"), ".ctor", 41L)), + NewObj(MemberRefData(TypeRefData(10L, "Bytes", "Expload.Pravda"), ".ctor", 33L)), StLoc1, LdcI43, - NewArr(TypeRefData(6L, "Byte", "System")), + NewArr(TypeRefData(6L, "SByte", "System")), Dup, - LdToken(FieldData(307, "B470CF972A0D84FBAEEEDB51A963A902269417E8", 119L)), + LdToken(FieldData(6, 307, "B470CF972A0D84FBAEEEDB51A963A902269417E8", 114L)), Call( MemberRefData( TypeRefData(6L, "RuntimeHelpers", "System.Runtime.CompilerServices"), "InitializeArray", - 33L + 25L ) ), - NewObj(MemberRefData(TypeRefData(10L, "Bytes", "Expload.Pravda"), ".ctor", 41L)), + NewObj(MemberRefData(TypeRefData(10L, "Bytes", "Expload.Pravda"), ".ctor", 33L)), StLoc2, LdLoc0, LdcI40, - LdElemU1, - StLoc3, + LdElemI1, + Pop, LdLoc0, LdcI42, - LdElemU1, - StLocS(4), + LdElemI1, + Pop, LdLoc1, LdcI41, - CallVirt(MemberRefData(TypeRefData(10L, "Bytes", "Expload.Pravda"), "get_Item", 47L)), - StLocS(5), + CallVirt(MemberRefData(TypeRefData(10L, "Bytes", "Expload.Pravda"), "get_Item", 39L)), + Pop, LdLoc2, LdcI41, - CallVirt(MemberRefData(TypeRefData(10L, "Bytes", "Expload.Pravda"), "get_Item", 47L)), - StLocS(6), + CallVirt(MemberRefData(TypeRefData(10L, "Bytes", "Expload.Pravda"), "get_Item", 39L)), + Pop, LdLoc1, LdcI41, LdcI42, - CallVirt(MemberRefData(TypeRefData(10L, "Bytes", "Expload.Pravda"), "Slice", 52L)), - StLocS(7), + CallVirt(MemberRefData(TypeRefData(10L, "Bytes", "Expload.Pravda"), "Slice", 44L)), + Pop, LdArg0, - LdFld(FieldData(6, "bytes", 109L)), + LdFld(FieldData(0, 1, "BytesMapping", 100L)), LdLoc1, LdLoc2, - CallVirt(MemberRefData(TypeSpecData(59L), "put", 68L)), - Nop, + CallVirt(MemberRefData(TypeSpecData(51L), "set_Item", 60L)), LdArg0, - LdFld(FieldData(6, "bytes", 109L)), + LdFld(FieldData(0, 1, "BytesMapping", 100L)), LdcI43, - NewArr(TypeRefData(6L, "Byte", "System")), + NewArr(TypeRefData(6L, "SByte", "System")), Dup, - LdToken(FieldData(307, "1EAFE5ED57A26A58369E0ECC65DD21A143D475E1", 119L)), + LdToken(FieldData(2, 307, "1EAFE5ED57A26A58369E0ECC65DD21A143D475E1", 114L)), Call( MemberRefData( TypeRefData(6L, "RuntimeHelpers", "System.Runtime.CompilerServices"), "InitializeArray", - 33L + 25L ) ), - NewObj(MemberRefData(TypeRefData(10L, "Bytes", "Expload.Pravda"), ".ctor", 41L)), - CallVirt(MemberRefData(TypeSpecData(59L), "exists", 76L)), - StLocS(9), - LdLocS(9), - BrFalseS(37), - Nop, + NewObj(MemberRefData(TypeRefData(10L, "Bytes", "Expload.Pravda"), ".ctor", 33L)), + CallVirt(MemberRefData(TypeSpecData(51L), "ContainsKey", 68L)), + BrFalseS(34), LdArg0, - LdFld(FieldData(6, "bytes", 109L)), + LdFld(FieldData(0, 1, "BytesMapping", 100L)), LdLoc1, LdcI43, - NewArr(TypeRefData(6L, "Byte", "System")), + NewArr(TypeRefData(6L, "SByte", "System")), Dup, - LdToken(FieldData(307, "B470CF972A0D84FBAEEEDB51A963A902269417E8", 119L)), + LdToken(FieldData(6, 307, "B470CF972A0D84FBAEEEDB51A963A902269417E8", 114L)), Call( MemberRefData( TypeRefData(6L, "RuntimeHelpers", "System.Runtime.CompilerServices"), "InitializeArray", - 33L + 25L ) ), - NewObj(MemberRefData(TypeRefData(10L, "Bytes", "Expload.Pravda"), ".ctor", 41L)), - CallVirt(MemberRefData(TypeSpecData(59L), "put", 68L)), - Nop, - Nop, + NewObj(MemberRefData(TypeRefData(10L, "Bytes", "Expload.Pravda"), ".ctor", 33L)), + CallVirt(MemberRefData(TypeSpecData(51L), "set_Item", 60L)), LdLoc0, LdcI40, LdcI42, @@ -116,8 +119,8 @@ methods: |- LdcI41, StElemI1, LdLoc1, - CallVirt(MemberRefData(TypeRefData(10L, "Bytes", "Expload.Pravda"), "Length", 82L)), - StLocS(8), + CallVirt(MemberRefData(TypeRefData(10L, "Bytes", "Expload.Pravda"), "Length", 74L)), + Pop, Ret ), 5, @@ -125,40 +128,39 @@ methods: |- ), Method( List( - Nop, LdcI43, NewArr(TypeRefData(6L, "Char", "System")), Dup, - LdToken(FieldData(307, "9F04F41A848514162050E3D68C1A7ABB441DC2B5", 131L)), + LdToken(FieldData(5, 307, "9F04F41A848514162050E3D68C1A7ABB441DC2B5", 118L)), Call( MemberRefData( TypeRefData(6L, "RuntimeHelpers", "System.Runtime.CompilerServices"), "InitializeArray", - 33L + 25L ) ), StLoc0, LdcI43, NewArr(TypeRefData(6L, "Int32", "System")), Dup, - LdToken(FieldData(307, "E429CCA3F703A39CC5954A6572FEC9086135B34E", 127L)), + LdToken(FieldData(7, 307, "E429CCA3F703A39CC5954A6572FEC9086135B34E", 122L)), Call( MemberRefData( TypeRefData(6L, "RuntimeHelpers", "System.Runtime.CompilerServices"), "InitializeArray", - 33L + 25L ) ), StLoc1, LdcI43, NewArr(TypeRefData(6L, "Double", "System")), Dup, - LdToken(FieldData(307, "380E84549CB845604C318E8E14B73622CC10AF42", 123L)), + LdToken(FieldData(3, 307, "380E84549CB845604C318E8E14B73622CC10AF42", 110L)), Call( MemberRefData( TypeRefData(6L, "RuntimeHelpers", "System.Runtime.CompilerServices"), "InitializeArray", - 33L + 25L ) ), StLoc2, @@ -178,14 +180,14 @@ methods: |- StElemRef, StLoc3, LdcI43, - NewArr(TypeRefData(6L, "UInt32", "System")), + NewArr(TypeRefData(6L, "Int64", "System")), Dup, - LdToken(FieldData(307, "8CFA957D76B6E190580D284C12F31AA6E3E2D41C", 127L)), + LdToken(FieldData(1, 307, "168D0BA38783EF14943E28073C750DBD4B83BDCC", 110L)), Call( MemberRefData( TypeRefData(6L, "RuntimeHelpers", "System.Runtime.CompilerServices"), "InitializeArray", - 33L + 25L ) ), StLocS(4), @@ -208,25 +210,25 @@ methods: |- LdLocS(4), LdcI41, LdcI47, - StElemI4, + ConvI8, + StElemI8, LdLoc3, LdLen, ConvI4, - StLocS(5), + Pop, Ret ), 4, - Some(86L) + Some(78L) ), - Method(List(Nop, Ret), 0, None), + Method(List(Ret), 0, None), Method( List( LdArg0, - NewObj(MemberRefData(TypeSpecData(59L), ".ctor", 6L)), - StFld(FieldData(6, "bytes", 109L)), + NewObj(MemberRefData(TypeSpecData(51L), ".ctor", 6L)), + StFld(FieldData(0, 1, "BytesMapping", 100L)), LdArg0, Call(MemberRefData(TypeRefData(6L, "Object", "System"), ".ctor", 6L)), - Nop, Ret ), 0, @@ -253,21 +255,14 @@ signatures: |- 16L, LocalVarSig( List( - LocalVar(Arr(U1, ArrayShape(1, List(), List())), false), - LocalVar(Cls(TypeRefData(10L, "Bytes", "Expload.Pravda")), false), + LocalVar(Arr(I1, ArrayShape(1, List(), List())), false), LocalVar(Cls(TypeRefData(10L, "Bytes", "Expload.Pravda")), false), - LocalVar(U1, false), - LocalVar(U1, false), - LocalVar(U1, false), - LocalVar(U1, false), - LocalVar(Cls(TypeRefData(10L, "Bytes", "Expload.Pravda")), false), - LocalVar(I4, false), - LocalVar(Boolean, false) + LocalVar(Cls(TypeRefData(10L, "Bytes", "Expload.Pravda")), false) ) ) ), ( - 33L, + 25L, MethodRefDefSig( false, false, @@ -282,7 +277,7 @@ signatures: |- ) ), ( - 41L, + 33L, MethodRefDefSig( true, false, @@ -290,12 +285,12 @@ signatures: |- false, 0, Tpe(Void, false), - List(Tpe(Arr(U1, ArrayShape(1, List(), List())), false)) + List(Tpe(Arr(I1, ArrayShape(1, List(), List())), false)) ) ), - (47L, MethodRefDefSig(true, false, false, false, 0, Tpe(U1, false), List(Tpe(I4, false)))), + (39L, MethodRefDefSig(true, false, false, false, 0, Tpe(I1, false), List(Tpe(I4, false)))), ( - 52L, + 44L, MethodRefDefSig( true, false, @@ -307,7 +302,7 @@ signatures: |- ) ), ( - 59L, + 51L, TypeSig( Tpe( Generic( @@ -322,7 +317,7 @@ signatures: |- ) ), ( - 68L, + 60L, MethodRefDefSig( true, false, @@ -334,25 +329,24 @@ signatures: |- ) ), ( - 76L, + 68L, MethodRefDefSig(true, false, false, false, 0, Tpe(Boolean, false), List(Tpe(Var(0), false))) ), - (82L, MethodRefDefSig(true, false, false, false, 0, Tpe(I4, false), List())), + (74L, MethodRefDefSig(true, false, false, false, 0, Tpe(I4, false), List())), ( - 86L, + 78L, LocalVarSig( List( LocalVar(Arr(Char, ArrayShape(1, List(), List())), false), LocalVar(Arr(I4, ArrayShape(1, List(), List())), false), LocalVar(Arr(R8, ArrayShape(1, List(), List())), false), LocalVar(Arr(String, ArrayShape(1, List(), List())), false), - LocalVar(Arr(U4, ArrayShape(1, List(), List())), false), - LocalVar(I4, false) + LocalVar(Arr(I8, ArrayShape(1, List(), List())), false) ) ) ), ( - 109L, + 100L, FieldSig( Generic( Cls(TypeRefData(10L, "Mapping`2", "Expload.Pravda")), @@ -364,13 +358,13 @@ signatures: |- ) ), ( - 119L, + 110L, FieldSig( ValueTpe( TypeDefData( - 3, + 6, 275, - "__StaticArrayInitTypeSize=3", + "__StaticArrayInitTypeSize=24", "", TypeRefData(6L, "ValueType", "System"), Vector(), @@ -380,13 +374,13 @@ signatures: |- ) ), ( - 123L, + 114L, FieldSig( ValueTpe( TypeDefData( - 6, + 3, 275, - "__StaticArrayInitTypeSize=24", + "__StaticArrayInitTypeSize=3", "", TypeRefData(6L, "ValueType", "System"), Vector(), @@ -396,13 +390,13 @@ signatures: |- ) ), ( - 127L, + 118L, FieldSig( ValueTpe( TypeDefData( - 5, + 4, 275, - "__StaticArrayInitTypeSize=12", + "__StaticArrayInitTypeSize=6", "", TypeRefData(6L, "ValueType", "System"), Vector(), @@ -412,13 +406,13 @@ signatures: |- ) ), ( - 131L, + 122L, FieldSig( ValueTpe( TypeDefData( - 4, + 5, 275, - "__StaticArrayInitTypeSize=6", + "__StaticArrayInitTypeSize=12", "", TypeRefData(6L, "ValueType", "System"), Vector(), @@ -427,5 +421,5 @@ signatures: |- ) ) ), - (135L, MethodRefDefSig(false, false, false, false, 0, Tpe(Void, false), List())) + (126L, MethodRefDefSig(false, false, false, false, 0, Tpe(Void, false), List())) ) diff --git a/dotnet/src/test/resources/parser/Block.prs b/dotnet/src/test/resources/parser/Block.prs new file mode 100644 index 00000000..4fa058ac --- /dev/null +++ b/dotnet/src/test/resources/parser/Block.prs @@ -0,0 +1,75 @@ +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: Block.exe + sources: + - Pravda.dll + - dotnet-tests/resources/Block.cs + optimize: true +--- +methods: |- + List( + Method( + List(Call(MemberRefData(TypeRefData(10L, "Info", "Expload.Pravda"), "Height", 16L)), Ret), + 0, + None + ), + Method( + List(Call(MemberRefData(TypeRefData(10L, "Info", "Expload.Pravda"), "LastBlockHash", 20L)), Ret), + 0, + None + ), + Method(List(Ret), 0, None), + Method( + List(LdArg0, Call(MemberRefData(TypeRefData(6L, "Object", "System"), ".ctor", 6L)), Ret), + 0, + None + ) + ) +signatures: |- + List( + (1L, MethodRefDefSig(true, false, false, false, 0, Tpe(Void, false), List(Tpe(I4, false)))), + (6L, MethodRefDefSig(true, false, false, false, 0, Tpe(Void, false), List())), + ( + 10L, + MethodRefDefSig( + true, + false, + false, + false, + 0, + Tpe(Void, false), + List(Tpe(ValueTpe(TypeRefData(15L, "DebuggingModes", "")), false)) + ) + ), + (16L, MethodRefDefSig(false, false, false, false, 0, Tpe(I8, false), List())), + ( + 20L, + MethodRefDefSig( + false, + false, + false, + false, + 0, + Tpe(Cls(TypeRefData(10L, "Bytes", "Expload.Pravda")), false), + List() + ) + ), + (34L, MethodRefDefSig(true, false, false, false, 0, Tpe(I8, false), List())), + ( + 38L, + MethodRefDefSig( + true, + false, + false, + false, + 0, + Tpe(Cls(TypeRefData(10L, "Bytes", "Expload.Pravda")), false), + List() + ) + ), + (43L, MethodRefDefSig(false, false, false, false, 0, Tpe(Void, false), List())) + ) diff --git a/dotnet/src/test/resources/parser/Callers.prs b/dotnet/src/test/resources/parser/Callers.prs new file mode 100644 index 00000000..20e71a44 --- /dev/null +++ b/dotnet/src/test/resources/parser/Callers.prs @@ -0,0 +1,76 @@ +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: Callers.exe + sources: + - Pravda.dll + - dotnet-tests/resources/Callers.cs + optimize: true +--- +methods: |- + List( + Method( + List( + Call(MemberRefData(TypeRefData(10L, "Info", "Expload.Pravda"), "Callers", 16L)), + LdcI40, + LdElemRef, + Ret + ), + 0, + None + ), + Method(List(Ret), 0, None), + Method( + List(LdArg0, Call(MemberRefData(TypeRefData(6L, "Object", "System"), ".ctor", 6L)), Ret), + 0, + None + ) + ) +signatures: |- + List( + (1L, MethodRefDefSig(true, false, false, false, 0, Tpe(Void, false), List(Tpe(I4, false)))), + (6L, MethodRefDefSig(true, false, false, false, 0, Tpe(Void, false), List())), + ( + 10L, + MethodRefDefSig( + true, + false, + false, + false, + 0, + Tpe(Void, false), + List(Tpe(ValueTpe(TypeRefData(15L, "DebuggingModes", "")), false)) + ) + ), + ( + 16L, + MethodRefDefSig( + false, + false, + false, + false, + 0, + Tpe( + Arr(Cls(TypeRefData(10L, "Bytes", "Expload.Pravda")), ArrayShape(1, List(), List())), + false + ), + List() + ) + ), + ( + 31L, + MethodRefDefSig( + true, + false, + false, + false, + 0, + Tpe(Cls(TypeRefData(10L, "Bytes", "Expload.Pravda")), false), + List() + ) + ), + (36L, MethodRefDefSig(false, false, false, false, 0, Tpe(Void, false), List())) + ) diff --git a/dotnet/src/test/resources/parser/Compare.prs b/dotnet/src/test/resources/parser/Compare.prs new file mode 100644 index 00000000..d86ad755 --- /dev/null +++ b/dotnet/src/test/resources/parser/Compare.prs @@ -0,0 +1,450 @@ +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: Compare.exe + sources: + - Pravda.dll + - dotnet-tests/resources/Compare.cs + optimize: true +--- +methods: |- + List( + Method( + List( + LdcI41, + StLoc0, + LdcI42, + StLoc1, + LdcI43, + StLoc2, + LdcI44, + StLoc3, + LdcI45, + ConvI8, + StLocS(4), + LdcI46, + ConvI8, + StLocS(5), + LdcI41, + StLocS(6), + LdLocS(6), + LdLoc0, + LdLoc1, + Ceq, + And, + StLocS(6), + LdLocS(6), + LdLoc0, + LdLoc2, + Ceq, + And, + StLocS(6), + LdLocS(6), + LdLoc2, + LdLoc3, + Ceq, + And, + StLocS(6), + LdLocS(6), + LdLoc2, + ConvI8, + LdLocS(4), + Ceq, + And, + StLocS(6), + LdLocS(6), + LdLocS(4), + LdLocS(5), + Ceq, + And, + StLocS(6), + LdLoc0, + LdLoc1, + BneUnS(3), + LdcI41, + StLocS(6), + LdLoc0, + LdLoc2, + BneUnS(3), + LdcI41, + StLocS(6), + LdLoc2, + LdLoc3, + BneUnS(3), + LdcI41, + StLocS(6), + LdLoc2, + ConvI8, + LdLocS(4), + BneUnS(3), + LdcI41, + StLocS(6), + LdLocS(4), + LdLocS(5), + BneUnS(3), + LdcI41, + StLocS(6), + LdLocS(6), + LdLoc0, + LdLoc1, + Clt, + LdcI40, + Ceq, + And, + StLocS(6), + LdLocS(6), + LdLoc0, + LdLoc2, + Clt, + LdcI40, + Ceq, + And, + StLocS(6), + LdLocS(6), + LdLoc2, + LdLoc3, + Clt, + LdcI40, + Ceq, + And, + StLocS(6), + LdLocS(6), + LdLoc2, + ConvI8, + LdLocS(4), + Clt, + LdcI40, + Ceq, + And, + StLocS(6), + LdLocS(6), + LdLocS(4), + LdLocS(5), + Clt, + LdcI40, + Ceq, + And, + StLocS(6), + LdLoc0, + LdLoc1, + BltS(3), + LdcI41, + StLocS(6), + LdLoc0, + LdLoc2, + BltS(3), + LdcI41, + StLocS(6), + LdLoc2, + LdLoc3, + BltS(3), + LdcI41, + StLocS(6), + LdLoc2, + ConvI8, + LdLocS(4), + BltS(3), + LdcI41, + StLocS(6), + LdLocS(4), + LdLocS(5), + BltS(3), + LdcI41, + StLocS(6), + LdLocS(6), + LdLoc0, + LdLoc1, + Cgt, + LdcI40, + Ceq, + And, + StLocS(6), + LdLocS(6), + LdLoc0, + LdLoc2, + Cgt, + LdcI40, + Ceq, + And, + StLocS(6), + LdLocS(6), + LdLoc2, + LdLoc3, + Cgt, + LdcI40, + Ceq, + And, + StLocS(6), + LdLocS(6), + LdLoc2, + ConvI8, + LdLocS(4), + Cgt, + LdcI40, + Ceq, + And, + StLocS(6), + LdLocS(6), + LdLocS(4), + LdLocS(5), + Cgt, + LdcI40, + Ceq, + And, + StLocS(6), + LdLoc0, + LdLoc1, + BgtS(3), + LdcI41, + StLocS(6), + LdLoc0, + LdLoc2, + BgtS(3), + LdcI41, + StLocS(6), + LdLoc2, + LdLoc3, + BgtS(3), + LdcI41, + StLocS(6), + LdLoc2, + ConvI8, + LdLocS(4), + BgtS(3), + LdcI41, + StLocS(6), + LdLocS(4), + LdLocS(5), + BgtS(3), + LdcI41, + StLocS(6), + LdLocS(6), + LdLoc0, + LdLoc1, + Ceq, + LdcI40, + Ceq, + And, + StLocS(6), + LdLocS(6), + LdLoc0, + LdLoc2, + Ceq, + LdcI40, + Ceq, + And, + StLocS(6), + LdLocS(6), + LdLoc2, + LdLoc3, + Ceq, + LdcI40, + Ceq, + And, + StLocS(6), + LdLocS(6), + LdLoc2, + ConvI8, + LdLocS(4), + Ceq, + LdcI40, + Ceq, + And, + StLocS(6), + LdLocS(6), + LdLocS(4), + LdLocS(5), + Ceq, + LdcI40, + Ceq, + And, + StLocS(6), + LdLoc0, + LdLoc1, + BeqS(3), + LdcI41, + StLocS(6), + LdLoc0, + LdLoc2, + BeqS(3), + LdcI41, + StLocS(6), + LdLoc2, + LdLoc3, + BeqS(3), + LdcI41, + StLocS(6), + LdLoc2, + ConvI8, + LdLocS(4), + BeqS(3), + LdcI41, + StLocS(6), + LdLocS(4), + LdLocS(5), + BeqS(3), + LdcI41, + StLocS(6), + LdLocS(6), + LdLoc0, + LdLoc1, + Cgt, + And, + StLocS(6), + LdLocS(6), + LdLoc0, + LdLoc2, + Cgt, + And, + StLocS(6), + LdLocS(6), + LdLoc2, + LdLoc3, + Cgt, + And, + StLocS(6), + LdLocS(6), + LdLoc2, + ConvI8, + LdLocS(4), + Cgt, + And, + StLocS(6), + LdLocS(6), + LdLocS(4), + LdLocS(5), + Cgt, + And, + StLocS(6), + LdLoc0, + LdLoc1, + BleS(3), + LdcI41, + StLocS(6), + LdLoc0, + LdLoc2, + BleS(3), + LdcI41, + StLocS(6), + LdLoc2, + LdLoc3, + BleS(3), + LdcI41, + StLocS(6), + LdLoc2, + ConvI8, + LdLocS(4), + BleS(3), + LdcI41, + StLocS(6), + LdLocS(4), + LdLocS(5), + BleS(3), + LdcI41, + StLocS(6), + LdLocS(6), + LdLoc0, + LdLoc1, + Clt, + And, + StLocS(6), + LdLocS(6), + LdLoc0, + LdLoc2, + Clt, + And, + StLocS(6), + LdLocS(6), + LdLoc2, + LdLoc3, + Clt, + And, + StLocS(6), + LdLocS(6), + LdLoc2, + ConvI8, + LdLocS(4), + Clt, + And, + StLocS(6), + LdLocS(6), + LdLocS(4), + LdLocS(5), + Clt, + And, + StLocS(6), + LdLoc0, + LdLoc1, + BgeS(3), + LdcI41, + StLocS(6), + LdLoc0, + LdLoc2, + BgeS(3), + LdcI41, + StLocS(6), + LdLoc2, + LdLoc3, + BgeS(3), + LdcI41, + StLocS(6), + LdLoc2, + ConvI8, + LdLocS(4), + BgeS(3), + LdcI41, + StLocS(6), + LdLocS(4), + LdLocS(5), + BgeS(3), + LdcI41, + StLocS(6), + LdLocS(6), + Ret + ), + 3, + Some(16L) + ), + Method(List(Ret), 0, None), + Method( + List(LdArg0, Call(MemberRefData(TypeRefData(6L, "Object", "System"), ".ctor", 6L)), Ret), + 0, + None + ) + ) +signatures: |- + List( + (1L, MethodRefDefSig(true, false, false, false, 0, Tpe(Void, false), List(Tpe(I4, false)))), + (6L, MethodRefDefSig(true, false, false, false, 0, Tpe(Void, false), List())), + ( + 10L, + MethodRefDefSig( + true, + false, + false, + false, + 0, + Tpe(Void, false), + List(Tpe(ValueTpe(TypeRefData(15L, "DebuggingModes", "")), false)) + ) + ), + ( + 16L, + LocalVarSig( + List( + LocalVar(I4, false), + LocalVar(I4, false), + LocalVar(I2, false), + LocalVar(I2, false), + LocalVar(I8, false), + LocalVar(I8, false), + LocalVar(Boolean, false) + ) + ) + ), + (35L, MethodRefDefSig(true, false, false, false, 0, Tpe(Boolean, false), List())), + (39L, MethodRefDefSig(false, false, false, false, 0, Tpe(Void, false), List())) + ) diff --git a/dotnet/src/test/resources/parser/ConcatStrings.prs b/dotnet/src/test/resources/parser/ConcatStrings.prs new file mode 100644 index 00000000..ff5793d5 --- /dev/null +++ b/dotnet/src/test/resources/parser/ConcatStrings.prs @@ -0,0 +1,404 @@ +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: ConcatStrings.exe + sources: + - Pravda.dll + - dotnet-tests/resources/ConcatStrings.cs + optimize: true +--- +methods: |- + List( + Method( + List( + LdStr("s"), + StLoc0, + LdLoc0, + LdLoc0, + LdStr("2"), + Call(MemberRefData(TypeRefData(6L, "String", "System"), "Concat", 29L)), + StLoc1, + LdLoc0, + LdLoc0, + LdLoc0, + LdStr("3"), + Call(MemberRefData(TypeRefData(6L, "String", "System"), "Concat", 36L)), + StLoc2, + LdcI45, + NewArr(TypeRefData(6L, "String", "System")), + Dup, + LdcI40, + LdLoc0, + StElemRef, + Dup, + LdcI41, + LdLoc0, + StElemRef, + Dup, + LdcI42, + LdLoc0, + StElemRef, + Dup, + LdcI43, + LdLoc0, + StElemRef, + Dup, + LdcI44, + LdStr("4"), + StElemRef, + Call(MemberRefData(TypeRefData(6L, "String", "System"), "Concat", 44L)), + StLoc3, + LdcI46, + NewArr(TypeRefData(6L, "String", "System")), + Dup, + LdcI40, + LdLoc0, + StElemRef, + Dup, + LdcI41, + LdLoc0, + StElemRef, + Dup, + LdcI42, + LdLoc0, + StElemRef, + Dup, + LdcI43, + LdLoc0, + StElemRef, + Dup, + LdcI44, + LdLoc0, + StElemRef, + Dup, + LdcI45, + LdStr("5"), + StElemRef, + Call(MemberRefData(TypeRefData(6L, "String", "System"), "Concat", 44L)), + StLocS(4), + LdcI47, + NewArr(TypeRefData(6L, "String", "System")), + Dup, + LdcI40, + LdLoc0, + StElemRef, + Dup, + LdcI41, + LdLoc0, + StElemRef, + Dup, + LdcI42, + LdLoc0, + StElemRef, + Dup, + LdcI43, + LdLoc0, + StElemRef, + Dup, + LdcI44, + LdLoc0, + StElemRef, + Dup, + LdcI45, + LdLoc0, + StElemRef, + Dup, + LdcI46, + LdStr("6"), + StElemRef, + Call(MemberRefData(TypeRefData(6L, "String", "System"), "Concat", 44L)), + StLocS(5), + LdcI48, + NewArr(TypeRefData(6L, "String", "System")), + Dup, + LdcI40, + LdLoc0, + StElemRef, + Dup, + LdcI41, + LdLoc0, + StElemRef, + Dup, + LdcI42, + LdLoc0, + StElemRef, + Dup, + LdcI43, + LdLoc0, + StElemRef, + Dup, + LdcI44, + LdLoc0, + StElemRef, + Dup, + LdcI45, + LdLoc0, + StElemRef, + Dup, + LdcI46, + LdLoc0, + StElemRef, + Dup, + LdcI47, + LdStr("7"), + StElemRef, + Call(MemberRefData(TypeRefData(6L, "String", "System"), "Concat", 44L)), + StLocS(6), + LdcI4S(9), + NewArr(TypeRefData(6L, "String", "System")), + Dup, + LdcI40, + LdLoc0, + StElemRef, + Dup, + LdcI41, + LdLoc0, + StElemRef, + Dup, + LdcI42, + LdLoc0, + StElemRef, + Dup, + LdcI43, + LdLoc0, + StElemRef, + Dup, + LdcI44, + LdLoc0, + StElemRef, + Dup, + LdcI45, + LdLoc0, + StElemRef, + Dup, + LdcI46, + LdLoc0, + StElemRef, + Dup, + LdcI47, + LdLoc0, + StElemRef, + Dup, + LdcI48, + LdStr("8"), + StElemRef, + Call(MemberRefData(TypeRefData(6L, "String", "System"), "Concat", 44L)), + StLocS(7), + LdcI4S(10), + NewArr(TypeRefData(6L, "String", "System")), + Dup, + LdcI40, + LdLoc0, + StElemRef, + Dup, + LdcI41, + LdLoc0, + StElemRef, + Dup, + LdcI42, + LdLoc0, + StElemRef, + Dup, + LdcI43, + LdLoc0, + StElemRef, + Dup, + LdcI44, + LdLoc0, + StElemRef, + Dup, + LdcI45, + LdLoc0, + StElemRef, + Dup, + LdcI46, + LdLoc0, + StElemRef, + Dup, + LdcI47, + LdLoc0, + StElemRef, + Dup, + LdcI48, + LdLoc0, + StElemRef, + Dup, + LdcI4S(9), + LdStr("9"), + StElemRef, + Call(MemberRefData(TypeRefData(6L, "String", "System"), "Concat", 44L)), + StLocS(8), + LdcI4S(11), + NewArr(TypeRefData(6L, "String", "System")), + Dup, + LdcI40, + LdLoc0, + StElemRef, + Dup, + LdcI41, + LdLoc0, + StElemRef, + Dup, + LdcI42, + LdLoc0, + StElemRef, + Dup, + LdcI43, + LdLoc0, + StElemRef, + Dup, + LdcI44, + LdLoc0, + StElemRef, + Dup, + LdcI45, + LdLoc0, + StElemRef, + Dup, + LdcI46, + LdLoc0, + StElemRef, + Dup, + LdcI47, + LdLoc0, + StElemRef, + Dup, + LdcI48, + LdLoc0, + StElemRef, + Dup, + LdcI4S(9), + LdLoc0, + StElemRef, + Dup, + LdcI4S(10), + LdStr("10"), + StElemRef, + Call(MemberRefData(TypeRefData(6L, "String", "System"), "Concat", 44L)), + StLocS(9), + LdcI4S(9), + NewArr(TypeRefData(6L, "String", "System")), + Dup, + LdcI40, + LdLoc1, + StElemRef, + Dup, + LdcI41, + LdLoc2, + StElemRef, + Dup, + LdcI42, + LdLoc3, + StElemRef, + Dup, + LdcI43, + LdLocS(4), + StElemRef, + Dup, + LdcI44, + LdLocS(5), + StElemRef, + Dup, + LdcI45, + LdLocS(6), + StElemRef, + Dup, + LdcI46, + LdLocS(7), + StElemRef, + Dup, + LdcI47, + LdLocS(8), + StElemRef, + Dup, + LdcI48, + LdLocS(9), + StElemRef, + Call(MemberRefData(TypeRefData(6L, "String", "System"), "Concat", 44L)), + Ret + ), + 4, + Some(16L) + ), + Method(List(Ret), 0, None), + Method( + List(LdArg0, Call(MemberRefData(TypeRefData(6L, "Object", "System"), ".ctor", 6L)), Ret), + 0, + None + ) + ) +signatures: |- + List( + (1L, MethodRefDefSig(true, false, false, false, 0, Tpe(Void, false), List(Tpe(I4, false)))), + (6L, MethodRefDefSig(true, false, false, false, 0, Tpe(Void, false), List())), + ( + 10L, + MethodRefDefSig( + true, + false, + false, + false, + 0, + Tpe(Void, false), + List(Tpe(ValueTpe(TypeRefData(15L, "DebuggingModes", "")), false)) + ) + ), + ( + 16L, + LocalVarSig( + List( + LocalVar(String, false), + LocalVar(String, false), + LocalVar(String, false), + LocalVar(String, false), + LocalVar(String, false), + LocalVar(String, false), + LocalVar(String, false), + LocalVar(String, false), + LocalVar(String, false), + LocalVar(String, false) + ) + ) + ), + ( + 29L, + MethodRefDefSig( + false, + false, + false, + false, + 0, + Tpe(String, false), + List(Tpe(String, false), Tpe(String, false), Tpe(String, false)) + ) + ), + ( + 36L, + MethodRefDefSig( + false, + false, + false, + false, + 0, + Tpe(String, false), + List(Tpe(String, false), Tpe(String, false), Tpe(String, false), Tpe(String, false)) + ) + ), + ( + 44L, + MethodRefDefSig( + false, + false, + false, + false, + 0, + Tpe(String, false), + List(Tpe(Arr(String, ArrayShape(1, List(), List())), false)) + ) + ), + (59L, MethodRefDefSig(true, false, false, false, 0, Tpe(String, false), List())), + (63L, MethodRefDefSig(false, false, false, false, 0, Tpe(Void, false), List())) + ) diff --git a/dotnet/src/test/resources/parser/hello_world.prs b/dotnet/src/test/resources/parser/Error.prs similarity index 72% rename from dotnet/src/test/resources/parser/hello_world.prs rename to dotnet/src/test/resources/parser/Error.prs index 1c616aef..d4be8d3d 100644 --- a/dotnet/src/test/resources/parser/hello_world.prs +++ b/dotnet/src/test/resources/parser/Error.prs @@ -1,20 +1,29 @@ -exe: hello_world.exe +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: Error.exe + sources: + - Pravda.dll + - dotnet-tests/resources/Error.cs + optimize: true --- methods: |- List( Method( List( - Nop, - LdStr("Hello World!"), + LdStr("Oops! There's no console in the blockchain!"), Call(MemberRefData(TypeRefData(6L, "Console", "System"), "WriteLine", 16L)), - Nop, Ret ), 0, None ), + Method(List(Ret), 0, None), Method( - List(LdArg0, Call(MemberRefData(TypeRefData(6L, "Object", "System"), ".ctor", 6L)), Nop, Ret), + List(LdArg0, Call(MemberRefData(TypeRefData(6L, "Object", "System"), ".ctor", 6L)), Ret), 0, None ) diff --git a/dotnet/src/test/resources/parser/event.prs b/dotnet/src/test/resources/parser/Event.prs similarity index 83% rename from dotnet/src/test/resources/parser/event.prs rename to dotnet/src/test/resources/parser/Event.prs index 41753e0d..bdc77d6d 100644 --- a/dotnet/src/test/resources/parser/event.prs +++ b/dotnet/src/test/resources/parser/Event.prs @@ -1,33 +1,39 @@ -exe: event.exe +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: Event.exe + sources: + - Pravda.dll + - dotnet-tests/resources/Event.cs + optimize: true --- methods: |- List( Method( List( - Nop, LdStr("my_event"), LdcI4(1234), Call( MethodSpecData(MemberRefData(TypeRefData(10L, "Log", "Expload.Pravda"), "Event", 16L), 24L) ), - Nop, LdStr("my_event"), LdStr("my_string"), Call( MethodSpecData(MemberRefData(TypeRefData(10L, "Log", "Expload.Pravda"), "Event", 16L), 28L) ), - Nop, LdStr("my_event"), LdcR8(2.0), Call( MethodSpecData(MemberRefData(TypeRefData(10L, "Log", "Expload.Pravda"), "Event", 16L), 32L) ), - Nop, LdStr("my_event"), LdcI44, - NewArr(TypeRefData(6L, "Byte", "System")), + NewArr(TypeRefData(6L, "SByte", "System")), Dup, - LdToken(FieldData(307, "12DADA1FFF4D4787ADE3333147202C3B443E376F", 64L)), + LdToken(FieldData(0, 307, "12DADA1FFF4D4787ADE3333147202C3B443E376F", 64L)), Call( MemberRefData( TypeRefData(6L, "RuntimeHelpers", "System.Runtime.CompilerServices"), @@ -39,15 +45,14 @@ methods: |- Call( MethodSpecData(MemberRefData(TypeRefData(10L, "Log", "Expload.Pravda"), "Event", 16L), 50L) ), - Nop, Ret ), 4, None ), - Method(List(Nop, Ret), 0, None), + Method(List(Ret), 0, None), Method( - List(LdArg0, Call(MemberRefData(TypeRefData(6L, "Object", "System"), ".ctor", 6L)), Nop, Ret), + List(LdArg0, Call(MemberRefData(TypeRefData(6L, "Object", "System"), ".ctor", 6L)), Ret), 0, None ) @@ -104,7 +109,7 @@ signatures: |- false, 0, Tpe(Void, false), - List(Tpe(Arr(U1, ArrayShape(1, List(), List())), false)) + List(Tpe(Arr(I1, ArrayShape(1, List(), List())), false)) ) ), (64L, FieldSig(I4)), diff --git a/dotnet/src/test/resources/parser/ExternalMethods.prs b/dotnet/src/test/resources/parser/ExternalMethods.prs new file mode 100644 index 00000000..e6c8654c --- /dev/null +++ b/dotnet/src/test/resources/parser/ExternalMethods.prs @@ -0,0 +1,161 @@ +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: ExternalMethods.dll + sources: + - Pravda.dll + - dotnet-tests/resources/ExternalMethods.cs + optimize: true + - target: ExternalMethodsCheck.exe + sources: + - Pravda.dll + - ExternalMethods.dll + - dotnet-tests/resources/ExternalMethodsCheck.cs + optimize: true + main-class: ExternalNamespace.ExternalMethodsCheck +--- +methods: |- + List( + Method( + List( + Call( + MemberRefData( + TypeRefData(14L, "ExternalProgramMethods", "ExternalNamespace"), + "GetInstance", + 22L + ) + ), + Dup, + LdcI42, + LdcI42, + CallVirt( + MemberRefData(TypeRefData(14L, "ExternalProgramMethods", "ExternalNamespace"), "Add", 27L) + ), + StLoc0, + Dup, + LdcI4S(10), + LdcI4S(10), + CallVirt( + MemberRefData(TypeRefData(14L, "ExternalProgramMethods", "ExternalNamespace"), "Add", 27L) + ), + StLoc1, + LdcI4(300), + LdcI4(300), + CallVirt( + MemberRefData(TypeRefData(14L, "ExternalProgramMethods", "ExternalNamespace"), "Add", 27L) + ), + StLoc2, + LdLoc0, + LdLoc1, + Add, + LdLoc2, + Add, + Ret + ), + 4, + Some(16L) + ), + Method( + List( + LdcI43, + LdcI43, + NewObj(MemberRefData(TypeRefData(14L, "ExternalMethods", "ExternalNamespace"), ".ctor", 33L)), + Dup, + CallVirt(MemberRefData(TypeRefData(14L, "ExternalMethods", "ExternalNamespace"), "Add", 39L)), + StLoc0, + LdcI4S(100), + CallVirt(MemberRefData(TypeRefData(14L, "ExternalMethods", "ExternalNamespace"), "Add", 43L)), + StLoc1, + LdcI4(1000), + LdcI4(1000), + Call(MemberRefData(TypeRefData(14L, "ExternalMethods", "ExternalNamespace"), "Add", 48L)), + StLoc2, + LdLoc0, + LdLoc1, + Add, + LdLoc2, + Add, + Ret + ), + 2, + Some(16L) + ), + Method(List(Ret), 0, None), + Method( + List(LdArg0, Call(MemberRefData(TypeRefData(6L, "Object", "System"), ".ctor", 6L)), Ret), + 0, + None + ) + ) +signatures: |- + List( + (1L, MethodRefDefSig(true, false, false, false, 0, Tpe(Void, false), List(Tpe(I4, false)))), + (6L, MethodRefDefSig(true, false, false, false, 0, Tpe(Void, false), List())), + ( + 10L, + MethodRefDefSig( + true, + false, + false, + false, + 0, + Tpe(Void, false), + List(Tpe(ValueTpe(TypeRefData(15L, "DebuggingModes", "")), false)) + ) + ), + (16L, LocalVarSig(List(LocalVar(I4, false), LocalVar(I4, false), LocalVar(I4, false)))), + ( + 22L, + MethodRefDefSig( + false, + false, + false, + false, + 0, + Tpe(Cls(TypeRefData(14L, "ExternalProgramMethods", "ExternalNamespace")), false), + List() + ) + ), + ( + 27L, + MethodRefDefSig( + true, + false, + false, + false, + 0, + Tpe(I4, false), + List(Tpe(I4, false), Tpe(I4, false)) + ) + ), + ( + 33L, + MethodRefDefSig( + true, + false, + false, + false, + 0, + Tpe(Void, false), + List(Tpe(I4, false), Tpe(I4, false)) + ) + ), + (39L, MethodRefDefSig(true, false, false, false, 0, Tpe(I4, false), List())), + (43L, MethodRefDefSig(true, false, false, false, 0, Tpe(I4, false), List(Tpe(I4, false)))), + ( + 48L, + MethodRefDefSig( + false, + false, + false, + false, + 0, + Tpe(I4, false), + List(Tpe(I4, false), Tpe(I4, false)) + ) + ), + (63L, MethodRefDefSig(false, false, false, false, 0, Tpe(Void, false), List())) + ) diff --git a/dotnet/src/test/resources/parser/If.prs b/dotnet/src/test/resources/parser/If.prs new file mode 100644 index 00000000..a3d07e98 --- /dev/null +++ b/dotnet/src/test/resources/parser/If.prs @@ -0,0 +1,106 @@ +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: If.exe + sources: + - Pravda.dll + - dotnet-tests/resources/If.cs + optimize: true +--- +methods: |- + List( + Method( + List( + LdcI4S(10), + StLoc0, + LdLoc0, + LdcI41, + BgeS(2), + LdcI44, + StLoc0, + LdLoc0, + LdcI45, + BleS(6), + LdLoc0, + LdcI46, + BleS(2), + LdcI47, + StLoc0, + LdLoc0, + LdcI40, + BleS(4), + LdcI44, + StLoc0, + BrS(2), + LdcI45, + StLoc0, + LdLoc0, + LdcI42, + BleS(8), + LdLoc0, + LdcI44, + BgeS(4), + LdcI46, + StLoc0, + BrS(2), + LdcI48, + StLoc0, + LdLoc0, + LdcI47, + BgtS(5), + LdLoc0, + LdcI4S(10), + BleS(4), + LdcI41, + StLoc0, + BrS(2), + LdcI40, + StLoc0, + LdLoc0, + LdcI41, + BleS(4), + LdLoc0, + LdcI43, + BltS(5), + LdLoc0, + LdcI4S(20), + BleS(3), + LdcI42, + StLoc0, + Ret, + LdcI43, + StLoc0, + Ret + ), + 2, + Some(16L) + ), + Method(List(Ret), 0, None), + Method( + List(LdArg0, Call(MemberRefData(TypeRefData(6L, "Object", "System"), ".ctor", 6L)), Ret), + 0, + None + ) + ) +signatures: |- + List( + (1L, MethodRefDefSig(true, false, false, false, 0, Tpe(Void, false), List(Tpe(I4, false)))), + (6L, MethodRefDefSig(true, false, false, false, 0, Tpe(Void, false), List())), + ( + 10L, + MethodRefDefSig( + true, + false, + false, + false, + 0, + Tpe(Void, false), + List(Tpe(ValueTpe(TypeRefData(15L, "DebuggingModes", "")), false)) + ) + ), + (16L, LocalVarSig(List(LocalVar(I4, false)))), + (29L, MethodRefDefSig(false, false, false, false, 0, Tpe(Void, false), List())) + ) diff --git a/dotnet/src/test/resources/parser/Inheritance.prs b/dotnet/src/test/resources/parser/Inheritance.prs new file mode 100644 index 00000000..5930cec9 --- /dev/null +++ b/dotnet/src/test/resources/parser/Inheritance.prs @@ -0,0 +1,130 @@ +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: Inheritance.exe + sources: + - Pravda.dll + - dotnet-tests/resources/Inheritance.cs + optimize: true +--- +methods: |- + List( + Method( + List(LdArg0, Call(MemberRefData(TypeRefData(6L, "Object", "System"), ".ctor", 6L)), Ret), + 0, + None + ), + Method( + List(LdArg0, CallVirt(MethodDefData(2, 0, 454, "Answer", 34L, Vector())), LdcI41, Add, Ret), + 0, + None + ), + Method(List(LdcI40, Ret), 0, None), + Method( + List( + LdArg0, + LdArg1, + Call(MethodDefData(0, 0, 6278, ".ctor", 1L, Vector(ParamData(0, 1, "val")))), + LdArg0, + LdArg1, + StFld(FieldData(0, 1, "AVal", 31L)), + Ret + ), + 0, + None + ), + Method(List(LdArg0, LdFld(FieldData(0, 1, "AVal", 31L)), LdcI4S(42), Add, Ret), 0, None), + Method( + List( + LdArg0, + LdArg1, + Call(MethodDefData(0, 0, 6278, ".ctor", 1L, Vector(ParamData(0, 1, "val")))), + LdArg0, + LdArg1, + StFld(FieldData(1, 1, "BVal", 31L)), + Ret + ), + 0, + None + ), + Method(List(LdArg0, LdFld(FieldData(1, 1, "BVal", 31L)), LdcI4S(43), Add, Ret), 0, None), + Method( + List( + LdcI4S(100), + NewObj(MethodDefData(3, 0, 6278, ".ctor", 1L, Vector(ParamData(0, 1, "aVal")))), + LdcI4(200), + NewObj(MethodDefData(5, 0, 6278, ".ctor", 1L, Vector(ParamData(0, 1, "bVal")))), + StLoc0, + Dup, + CallVirt(MethodDefData(2, 0, 454, "Answer", 34L, Vector())), + Pop, + LdLoc0, + CallVirt(MethodDefData(2, 0, 454, "Answer", 34L, Vector())), + Pop, + CallVirt(MethodDefData(1, 0, 454, "AnswerPlus1", 34L, Vector())), + LdLoc0, + CallVirt(MethodDefData(1, 0, 454, "AnswerPlus1", 34L, Vector())), + StLoc1, + LdLoc1, + Add, + Ret + ), + 2, + Some(16L) + ), + Method(List(Ret), 0, None), + Method( + List(LdArg0, Call(MemberRefData(TypeRefData(6L, "Object", "System"), ".ctor", 6L)), Ret), + 0, + None + ) + ) +signatures: |- + List( + (1L, MethodRefDefSig(true, false, false, false, 0, Tpe(Void, false), List(Tpe(I4, false)))), + (6L, MethodRefDefSig(true, false, false, false, 0, Tpe(Void, false), List())), + ( + 10L, + MethodRefDefSig( + true, + false, + false, + false, + 0, + Tpe(Void, false), + List(Tpe(ValueTpe(TypeRefData(15L, "DebuggingModes", "")), false)) + ) + ), + ( + 16L, + LocalVarSig( + List( + LocalVar( + Cls( + TypeDefData( + 1, + 1048577, + "Parent", + "", + TypeRefData(6L, "Object", "System"), + Vector(), + Vector( + MethodDefData(0, 0, 6278, ".ctor", 1L, Vector(ParamData(0, 1, "val"))), + MethodDefData(1, 0, 454, "AnswerPlus1", 34L, Vector()), + MethodDefData(2, 0, 454, "Answer", 34L, Vector()) + ) + ) + ), + false + ), + LocalVar(I4, false) + ) + ) + ), + (31L, FieldSig(I4)), + (34L, MethodRefDefSig(true, false, false, false, 0, Tpe(I4, false), List())), + (38L, MethodRefDefSig(false, false, false, false, 0, Tpe(Void, false), List())) + ) diff --git a/dotnet/src/test/resources/parser/IntBuffer.prs b/dotnet/src/test/resources/parser/IntBuffer.prs new file mode 100644 index 00000000..45bcfb5d --- /dev/null +++ b/dotnet/src/test/resources/parser/IntBuffer.prs @@ -0,0 +1,243 @@ +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: IntBuffer.exe + sources: + - Pravda.dll + - dotnet-tests/resources/IntBuffer.cs + optimize: true +--- +methods: |- + List( + Method( + List( + LdArg0, + Call(MemberRefData(TypeRefData(6L, "Object", "System"), ".ctor", 6L)), + LdArg0, + LdcI4S(16), + NewArr(TypeRefData(6L, "Int32", "System")), + StFld(FieldData(1, 1, "buffer", 59L)), + Ret + ), + 0, + None + ), + Method( + List( + LdArg0, + Call(MemberRefData(TypeRefData(6L, "Object", "System"), ".ctor", 6L)), + LdArg0, + LdArg1, + NewArr(TypeRefData(6L, "Int32", "System")), + StFld(FieldData(1, 1, "buffer", 59L)), + Ret + ), + 0, + None + ), + Method( + List( + LdArg0, + LdFld(FieldData(0, 1, "size", 56L)), + LdArg0, + LdFld(FieldData(1, 1, "buffer", 59L)), + LdLen, + ConvI4, + BneUnS(55), + LdArg0, + LdFld(FieldData(1, 1, "buffer", 59L)), + LdLen, + ConvI4, + LdcI42, + Mul, + LdcI41, + Add, + NewArr(TypeRefData(6L, "Int32", "System")), + StLoc0, + LdcI40, + StLoc1, + BrS(15), + LdLoc0, + LdLoc1, + LdArg0, + LdFld(FieldData(1, 1, "buffer", 59L)), + LdLoc1, + LdElemI4, + StElemI4, + LdLoc1, + LdcI41, + Add, + StLoc1, + LdLoc1, + LdArg0, + LdFld(FieldData(1, 1, "buffer", 59L)), + LdLen, + ConvI4, + BltS(-26), + LdArg0, + LdLoc0, + StFld(FieldData(1, 1, "buffer", 59L)), + LdArg0, + LdFld(FieldData(1, 1, "buffer", 59L)), + LdArg0, + LdFld(FieldData(0, 1, "size", 56L)), + LdArg1, + StElemI4, + LdArg0, + LdArg0, + LdFld(FieldData(0, 1, "size", 56L)), + LdcI41, + Add, + StFld(FieldData(0, 1, "size", 56L)), + Ret + ), + 4, + Some(21L) + ), + Method(List(LdArg0, LdFld(FieldData(1, 1, "buffer", 59L)), LdArg1, LdElemI4, Ret), 0, None), + Method( + List(LdArg0, LdFld(FieldData(1, 1, "buffer", 59L)), LdArg1, LdArg2, StElemI4, Ret), + 0, + None + ), + Method( + List( + LdcI42, + NewObj(MethodDefData(1, 0, 6278, ".ctor", 1L, Vector(ParamData(0, 1, "initSize")))), + Dup, + LdcI41, + CallVirt(MethodDefData(2, 0, 134, "Append", 1L, Vector(ParamData(0, 1, "elem")))), + Dup, + LdcI43, + CallVirt(MethodDefData(2, 0, 134, "Append", 1L, Vector(ParamData(0, 1, "elem")))), + Dup, + LdcI45, + CallVirt(MethodDefData(2, 0, 134, "Append", 1L, Vector(ParamData(0, 1, "elem")))), + Dup, + LdcI47, + CallVirt(MethodDefData(2, 0, 134, "Append", 1L, Vector(ParamData(0, 1, "elem")))), + Dup, + LdcI4S(9), + CallVirt(MethodDefData(2, 0, 134, "Append", 1L, Vector(ParamData(0, 1, "elem")))), + Dup, + LdcI4S(11), + CallVirt(MethodDefData(2, 0, 134, "Append", 1L, Vector(ParamData(0, 1, "elem")))), + Dup, + LdcI4S(13), + CallVirt(MethodDefData(2, 0, 134, "Append", 1L, Vector(ParamData(0, 1, "elem")))), + Dup, + LdcI4S(15), + CallVirt(MethodDefData(2, 0, 134, "Append", 1L, Vector(ParamData(0, 1, "elem")))), + Dup, + LdcI4S(17), + CallVirt(MethodDefData(2, 0, 134, "Append", 1L, Vector(ParamData(0, 1, "elem")))), + Dup, + LdcI40, + CallVirt(MethodDefData(3, 0, 2182, "get_Item", 63L, Vector(ParamData(0, 1, "i")))), + StLoc0, + Dup, + LdcI41, + CallVirt(MethodDefData(3, 0, 2182, "get_Item", 63L, Vector(ParamData(0, 1, "i")))), + StLoc1, + Dup, + LdcI42, + CallVirt(MethodDefData(3, 0, 2182, "get_Item", 63L, Vector(ParamData(0, 1, "i")))), + StLoc2, + Dup, + LdcI41, + LdcI4S(10), + CallVirt( + MethodDefData( + 4, + 0, + 2182, + "set_Item", + 68L, + Vector(ParamData(0, 1, "i"), ParamData(0, 2, "value")) + ) + ), + LdcI41, + CallVirt(MethodDefData(3, 0, 2182, "get_Item", 63L, Vector(ParamData(0, 1, "i")))), + StLoc3, + LdLoc0, + Call(MemberRefData(TypeRefData(6L, "Convert", "System"), "ToString", 34L)), + LdLoc1, + Call(MemberRefData(TypeRefData(6L, "Convert", "System"), "ToString", 34L)), + LdLoc2, + Call(MemberRefData(TypeRefData(6L, "Convert", "System"), "ToString", 34L)), + LdLoc3, + Call(MemberRefData(TypeRefData(6L, "Convert", "System"), "ToString", 34L)), + Call(MemberRefData(TypeRefData(6L, "String", "System"), "Concat", 39L)), + Ret + ), + 4, + Some(27L) + ), + Method(List(Ret), 0, None), + Method( + List(LdArg0, Call(MemberRefData(TypeRefData(6L, "Object", "System"), ".ctor", 6L)), Ret), + 0, + None + ) + ) +signatures: |- + List( + (1L, MethodRefDefSig(true, false, false, false, 0, Tpe(Void, false), List(Tpe(I4, false)))), + (6L, MethodRefDefSig(true, false, false, false, 0, Tpe(Void, false), List())), + ( + 10L, + MethodRefDefSig( + true, + false, + false, + false, + 0, + Tpe(Void, false), + List(Tpe(ValueTpe(TypeRefData(15L, "DebuggingModes", "")), false)) + ) + ), + (16L, MethodRefDefSig(true, false, false, false, 0, Tpe(Void, false), List(Tpe(String, false)))), + ( + 21L, + LocalVarSig(List(LocalVar(Arr(I4, ArrayShape(1, List(), List())), false), LocalVar(I4, false))) + ), + ( + 27L, + LocalVarSig( + List(LocalVar(I4, false), LocalVar(I4, false), LocalVar(I4, false), LocalVar(I4, false)) + ) + ), + (34L, MethodRefDefSig(false, false, false, false, 0, Tpe(String, false), List(Tpe(I4, false)))), + ( + 39L, + MethodRefDefSig( + false, + false, + false, + false, + 0, + Tpe(String, false), + List(Tpe(String, false), Tpe(String, false), Tpe(String, false), Tpe(String, false)) + ) + ), + (56L, FieldSig(I4)), + (59L, FieldSig(Arr(I4, ArrayShape(1, List(), List())))), + (63L, MethodRefDefSig(true, false, false, false, 0, Tpe(I4, false), List(Tpe(I4, false)))), + ( + 68L, + MethodRefDefSig( + true, + false, + false, + false, + 0, + Tpe(Void, false), + List(Tpe(I4, false), Tpe(I4, false)) + ) + ), + (74L, MethodRefDefSig(true, false, false, false, 0, Tpe(String, false), List())), + (78L, MethodRefDefSig(false, false, false, false, 0, Tpe(Void, false), List())) + ) diff --git a/dotnet/src/test/resources/parser/loop.prs b/dotnet/src/test/resources/parser/Loop.prs similarity index 62% rename from dotnet/src/test/resources/parser/loop.prs rename to dotnet/src/test/resources/parser/Loop.prs index e19f4da8..123d5cab 100644 --- a/dotnet/src/test/resources/parser/loop.prs +++ b/dotnet/src/test/resources/parser/Loop.prs @@ -1,52 +1,52 @@ -exe: loop.exe +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: Loop.exe + sources: + - Pravda.dll + - dotnet-tests/resources/Loop.cs + optimize: true --- methods: |- List( Method( List( - Nop, LdcI40, StLoc0, LdcI40, StLoc1, - BrS(10), - Nop, + BrS(8), LdLoc0, LdcI42, Add, StLoc0, - Nop, LdLoc1, LdcI41, Add, StLoc1, LdLoc1, LdcI4S(10), - Clt, - StLoc2, - LdLoc2, - BrTrueS(-19), - BrS(6), - Nop, + BltS(-13), + BrS(4), LdLoc0, LdcI42, Mul, StLoc0, - Nop, LdLoc0, LdcI4(10000), - Clt, - StLoc3, - LdLoc3, - BrTrueS(-18), + BltS(-12), + LdLoc0, Ret ), 2, Some(16L) ), - Method(List(Nop, Ret), 0, None), + Method(List(Ret), 0, None), Method( - List(LdArg0, Call(MemberRefData(TypeRefData(6L, "Object", "System"), ".ctor", 6L)), Nop, Ret), + List(LdArg0, Call(MemberRefData(TypeRefData(6L, "Object", "System"), ".ctor", 6L)), Ret), 0, None ) @@ -67,16 +67,7 @@ signatures: |- List(Tpe(ValueTpe(TypeRefData(15L, "DebuggingModes", "")), false)) ) ), - ( - 16L, - LocalVarSig( - List( - LocalVar(I4, false), - LocalVar(I4, false), - LocalVar(Boolean, false), - LocalVar(Boolean, false) - ) - ) - ), - (32L, MethodRefDefSig(false, false, false, false, 0, Tpe(Void, false), List())) + (16L, LocalVarSig(List(LocalVar(I4, false), LocalVar(I4, false)))), + (30L, MethodRefDefSig(true, false, false, false, 0, Tpe(I4, false), List())), + (34L, MethodRefDefSig(false, false, false, false, 0, Tpe(Void, false), List())) ) diff --git a/dotnet/src/test/resources/parser/loop_nested.prs b/dotnet/src/test/resources/parser/LoopNested.prs similarity index 59% rename from dotnet/src/test/resources/parser/loop_nested.prs rename to dotnet/src/test/resources/parser/LoopNested.prs index a20346c0..f2c14a7a 100644 --- a/dotnet/src/test/resources/parser/loop_nested.prs +++ b/dotnet/src/test/resources/parser/LoopNested.prs @@ -1,29 +1,31 @@ -exe: loop_nested.exe +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: LoopNested.exe + sources: + - Pravda.dll + - dotnet-tests/resources/LoopNested.cs + optimize: true --- methods: |- List( Method( List( - Nop, LdcI40, StLoc0, LdcI40, StLoc1, - BrS(51), - Nop, + BrS(34), LdcI40, StLoc2, - BrS(30), - Nop, + BrS(21), LdLoc0, LdcI42, Rem, - LdcI40, - Ceq, - StLoc3, - LdLoc3, - BrFalseS(14), - Nop, + BrTrueS(12), LdLoc0, LdLoc1, LdLoc2, @@ -32,50 +34,37 @@ methods: |- Rem, Add, StLoc0, - Nop, - Nop, LdLoc2, LdcI41, Add, StLoc2, LdLoc2, LdcI4S(20), - Clt, - StLocS(4), - LdLocS(4), - BrTrueS(-41), - Nop, + BltS(-26), LdLoc1, LdcI41, Add, StLoc1, LdLoc1, LdcI4S(10), - Clt, - StLocS(5), - LdLocS(5), - BrTrueS(-62), - BrS(6), - Nop, + BltS(-39), + BrS(4), LdLoc0, LdcI42, Mul, StLoc0, - Nop, LdLoc0, LdcI4(10000), - Clt, - StLocS(6), - LdLocS(6), - BrTrueS(-20), + BltS(-12), + LdLoc0, Ret ), 3, Some(16L) ), - Method(List(Nop, Ret), 0, None), + Method(List(Ret), 0, None), Method( - List(LdArg0, Call(MemberRefData(TypeRefData(6L, "Object", "System"), ".ctor", 6L)), Nop, Ret), + List(LdArg0, Call(MemberRefData(TypeRefData(6L, "Object", "System"), ".ctor", 6L)), Ret), 0, None ) @@ -96,19 +85,7 @@ signatures: |- List(Tpe(ValueTpe(TypeRefData(15L, "DebuggingModes", "")), false)) ) ), - ( - 16L, - LocalVarSig( - List( - LocalVar(I4, false), - LocalVar(I4, false), - LocalVar(I4, false), - LocalVar(Boolean, false), - LocalVar(Boolean, false), - LocalVar(Boolean, false), - LocalVar(Boolean, false) - ) - ) - ), + (16L, LocalVarSig(List(LocalVar(I4, false), LocalVar(I4, false), LocalVar(I4, false)))), + (31L, MethodRefDefSig(true, false, false, false, 0, Tpe(I4, false), List())), (35L, MethodRefDefSig(false, false, false, false, 0, Tpe(Void, false), List())) ) diff --git a/dotnet/src/test/resources/parser/Object.prs b/dotnet/src/test/resources/parser/Object.prs new file mode 100644 index 00000000..4fa1147d --- /dev/null +++ b/dotnet/src/test/resources/parser/Object.prs @@ -0,0 +1,107 @@ +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: Object.exe + sources: + - Pravda.dll + - dotnet-tests/resources/Object.cs + optimize: true +--- +methods: |- + List( + Method( + List( + LdArg0, + Call(MemberRefData(TypeRefData(6L, "Object", "System"), ".ctor", 6L)), + LdArg0, + LdArg1, + StFld(FieldData(0, 1, "AVal", 30L)), + Ret + ), + 0, + None + ), + Method(List(LdArg0, LdFld(FieldData(0, 1, "AVal", 30L)), LdcI4S(42), Add, Ret), 0, None), + Method( + List( + LdArg0, + Call(MemberRefData(TypeRefData(6L, "Object", "System"), ".ctor", 6L)), + LdArg0, + LdArg1, + StFld(FieldData(1, 1, "BVal", 30L)), + Ret + ), + 0, + None + ), + Method(List(LdArg0, LdFld(FieldData(1, 1, "BVal", 30L)), LdcI4S(43), Add, Ret), 0, None), + Method( + List( + LdcI4S(100), + NewObj(MethodDefData(0, 0, 6278, ".ctor", 1L, Vector(ParamData(0, 1, "aVal")))), + LdcI4(200), + NewObj(MethodDefData(2, 0, 6278, ".ctor", 1L, Vector(ParamData(0, 1, "bVal")))), + StLoc0, + CallVirt(MethodDefData(1, 0, 134, "AnswerA", 33L, Vector())), + LdLoc0, + CallVirt(MethodDefData(3, 0, 134, "AnswerB", 33L, Vector())), + Add, + Ret + ), + 2, + Some(16L) + ), + Method(List(Ret), 0, None), + Method( + List(LdArg0, Call(MemberRefData(TypeRefData(6L, "Object", "System"), ".ctor", 6L)), Ret), + 0, + None + ) + ) +signatures: |- + List( + (1L, MethodRefDefSig(true, false, false, false, 0, Tpe(Void, false), List(Tpe(I4, false)))), + (6L, MethodRefDefSig(true, false, false, false, 0, Tpe(Void, false), List())), + ( + 10L, + MethodRefDefSig( + true, + false, + false, + false, + 0, + Tpe(Void, false), + List(Tpe(ValueTpe(TypeRefData(15L, "DebuggingModes", "")), false)) + ) + ), + ( + 16L, + LocalVarSig( + List( + LocalVar( + Cls( + TypeDefData( + 2, + 1048577, + "B", + "", + TypeRefData(6L, "Object", "System"), + Vector(FieldData(1, 1, "BVal", 30L)), + Vector( + MethodDefData(2, 0, 6278, ".ctor", 1L, Vector(ParamData(0, 1, "bVal"))), + MethodDefData(3, 0, 134, "AnswerB", 33L, Vector()) + ) + ) + ), + false + ) + ) + ) + ), + (30L, FieldSig(I4)), + (33L, MethodRefDefSig(true, false, false, false, 0, Tpe(I4, false), List())), + (37L, MethodRefDefSig(false, false, false, false, 0, Tpe(Void, false), List())) + ) diff --git a/dotnet/src/test/resources/parser/ObjectGetSet.prs b/dotnet/src/test/resources/parser/ObjectGetSet.prs new file mode 100644 index 00000000..34968f4e --- /dev/null +++ b/dotnet/src/test/resources/parser/ObjectGetSet.prs @@ -0,0 +1,107 @@ +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: ObjectGetSet.exe + sources: + - Pravda.dll + - dotnet-tests/resources/ObjectGetSet.cs + optimize: true +--- +methods: |- + List( + Method(List(LdArg0, LdFld(FieldData(1, 1, "k__BackingField", 30L)), Ret), 0, None), + Method(List(LdArg0, LdArg1, StFld(FieldData(1, 1, "k__BackingField", 30L)), Ret), 0, None), + Method(List(LdArg0, LdArg1, StFld(FieldData(2, 1, "field3", 30L)), Ret), 0, None), + Method(List(LdArg0, LdFld(FieldData(2, 1, "field3", 30L)), Ret), 0, None), + Method( + List(LdArg0, Call(MemberRefData(TypeRefData(6L, "Object", "System"), ".ctor", 6L)), Ret), + 0, + None + ), + Method( + List( + NewObj(MethodDefData(4, 0, 6278, ".ctor", 6L, Vector())), + StLoc0, + LdLoc0, + LdcI43, + StFld(FieldData(0, 6, "field1", 30L)), + LdLoc0, + LdcI4S(20), + CallVirt(MethodDefData(1, 0, 2182, "set_field2", 1L, Vector(ParamData(0, 1, "value")))), + LdLoc0, + LdcI4S(100), + CallVirt(MethodDefData(2, 0, 134, "SetField3", 1L, Vector(ParamData(0, 1, "field3")))), + LdLoc0, + LdFld(FieldData(0, 6, "field1", 30L)), + LdLoc0, + CallVirt(MethodDefData(0, 0, 2182, "get_field2", 33L, Vector())), + Add, + LdLoc0, + CallVirt(MethodDefData(3, 0, 134, "GetField3", 33L, Vector())), + Add, + Ret + ), + 2, + Some(16L) + ), + Method(List(Ret), 0, None), + Method( + List(LdArg0, Call(MemberRefData(TypeRefData(6L, "Object", "System"), ".ctor", 6L)), Ret), + 0, + None + ) + ) +signatures: |- + List( + (1L, MethodRefDefSig(true, false, false, false, 0, Tpe(Void, false), List(Tpe(I4, false)))), + (6L, MethodRefDefSig(true, false, false, false, 0, Tpe(Void, false), List())), + ( + 10L, + MethodRefDefSig( + true, + false, + false, + false, + 0, + Tpe(Void, false), + List(Tpe(ValueTpe(TypeRefData(15L, "DebuggingModes", "")), false)) + ) + ), + ( + 16L, + LocalVarSig( + List( + LocalVar( + Cls( + TypeDefData( + 1, + 1048577, + "SomeClass", + "", + TypeRefData(6L, "Object", "System"), + Vector( + FieldData(0, 6, "field1", 30L), + FieldData(1, 1, "k__BackingField", 30L), + FieldData(2, 1, "field3", 30L) + ), + Vector( + MethodDefData(0, 0, 2182, "get_field2", 33L, Vector()), + MethodDefData(1, 0, 2182, "set_field2", 1L, Vector(ParamData(0, 1, "value"))), + MethodDefData(2, 0, 134, "SetField3", 1L, Vector(ParamData(0, 1, "field3"))), + MethodDefData(3, 0, 134, "GetField3", 33L, Vector()), + MethodDefData(4, 0, 6278, ".ctor", 6L, Vector()) + ) + ) + ), + false + ) + ) + ) + ), + (30L, FieldSig(I4)), + (33L, MethodRefDefSig(true, false, false, false, 0, Tpe(I4, false), List())), + (37L, MethodRefDefSig(false, false, false, false, 0, Tpe(Void, false), List())) + ) diff --git a/dotnet/src/test/resources/parser/pcall.prs b/dotnet/src/test/resources/parser/Pcall.prs similarity index 65% rename from dotnet/src/test/resources/parser/pcall.prs rename to dotnet/src/test/resources/parser/Pcall.prs index 5c6d6d9d..8d37ab28 100644 --- a/dotnet/src/test/resources/parser/pcall.prs +++ b/dotnet/src/test/resources/parser/Pcall.prs @@ -1,66 +1,71 @@ -exe: pcall.exe +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: PcallProgram.dll + sources: + - Pravda.dll + - dotnet-tests/resources/PcallProgram.cs + optimize: true + - target: Pcall.exe + sources: + - Pravda.dll + - PcallProgram.dll + - dotnet-tests/resources/Pcall.cs + optimize: true + main-class: PcallNamespace.Pcall --- methods: |- List( Method( List( - Nop, LdStr("1eaed20b7ce2b336043e4b340b031f95bb1ce6d935ef733ae4df1b66e1e3d91f"), - NewObj(MemberRefData(TypeRefData(10L, "Bytes", "Expload.Pravda"), ".ctor", 26L)), - StLoc0, - LdLoc0, + NewObj(MemberRefData(TypeRefData(10L, "Bytes", "Expload.Pravda"), ".ctor", 21L)), Call( MethodSpecData( - MemberRefData(TypeRefData(10L, "ProgramHelper", "Expload.Pravda"), "Program", 31L), - 39L + MemberRefData(TypeRefData(10L, "ProgramHelper", "Expload.Pravda"), "Program", 26L), + 34L ) ), LdcI41, LdcI42, - CallVirt( - MemberRefData(TypeRefData(14L, "MyAnotherProgram", "Expload.Pravda.Programs"), "Add", 44L) - ), - StLoc1, + CallVirt(MemberRefData(TypeRefData(14L, "PcallProgram", "PcallNamespace"), "Add", 39L)), + StLoc0, LdcI4S(32), - NewArr(TypeRefData(6L, "Byte", "System")), + NewArr(TypeRefData(6L, "SByte", "System")), Dup, - LdToken(FieldData(307, "AABC1596532EF46AA24B3AEE37458002516F48B8", 73L)), + LdToken(FieldData(0, 307, "AABC1596532EF46AA24B3AEE37458002516F48B8", 68L)), Call( MemberRefData( TypeRefData(6L, "RuntimeHelpers", "System.Runtime.CompilerServices"), "InitializeArray", - 50L + 45L ) ), - NewObj(MemberRefData(TypeRefData(10L, "Bytes", "Expload.Pravda"), ".ctor", 58L)), - StLoc2, - LdLoc2, + NewObj(MemberRefData(TypeRefData(10L, "Bytes", "Expload.Pravda"), ".ctor", 53L)), Call( MethodSpecData( - MemberRefData(TypeRefData(10L, "ProgramHelper", "Expload.Pravda"), "Program", 31L), - 39L + MemberRefData(TypeRefData(10L, "ProgramHelper", "Expload.Pravda"), "Program", 26L), + 34L ) ), LdcI43, LdcI44, - CallVirt( - MemberRefData(TypeRefData(14L, "MyAnotherProgram", "Expload.Pravda.Programs"), "Add", 44L) - ), - StLoc3, + CallVirt(MemberRefData(TypeRefData(14L, "PcallProgram", "PcallNamespace"), "Add", 39L)), + StLoc1, + LdLoc0, LdLoc1, - LdLoc3, Add, - StLocS(4), - BrS(0), - LdLocS(4), Ret ), 3, Some(16L) ), - Method(List(Nop, Ret), 0, None), + Method(List(Ret), 0, None), Method( - List(LdArg0, Call(MemberRefData(TypeRefData(6L, "Object", "System"), ".ctor", 6L)), Nop, Ret), + List(LdArg0, Call(MemberRefData(TypeRefData(6L, "Object", "System"), ".ctor", 6L)), Ret), 0, None ) @@ -81,21 +86,10 @@ signatures: |- List(Tpe(ValueTpe(TypeRefData(15L, "DebuggingModes", "")), false)) ) ), + (16L, LocalVarSig(List(LocalVar(I4, false), LocalVar(I4, false)))), + (21L, MethodRefDefSig(true, false, false, false, 0, Tpe(Void, false), List(Tpe(String, false)))), ( - 16L, - LocalVarSig( - List( - LocalVar(Cls(TypeRefData(10L, "Bytes", "Expload.Pravda")), false), - LocalVar(I4, false), - LocalVar(Cls(TypeRefData(10L, "Bytes", "Expload.Pravda")), false), - LocalVar(I4, false), - LocalVar(I4, false) - ) - ) - ), - (26L, MethodRefDefSig(true, false, false, false, 0, Tpe(Void, false), List(Tpe(String, false)))), - ( - 31L, + 26L, MethodRefDefSig( false, false, @@ -107,7 +101,7 @@ signatures: |- ) ), ( - 44L, + 39L, MethodRefDefSig( true, false, @@ -119,7 +113,7 @@ signatures: |- ) ), ( - 50L, + 45L, MethodRefDefSig( false, false, @@ -134,7 +128,7 @@ signatures: |- ) ), ( - 58L, + 53L, MethodRefDefSig( true, false, @@ -142,11 +136,11 @@ signatures: |- false, 0, Tpe(Void, false), - List(Tpe(Arr(U1, ArrayShape(1, List(), List())), false)) + List(Tpe(Arr(I1, ArrayShape(1, List(), List())), false)) ) ), ( - 73L, + 68L, FieldSig( ValueTpe( TypeDefData( @@ -161,6 +155,6 @@ signatures: |- ) ) ), - (77L, MethodRefDefSig(true, false, false, false, 0, Tpe(I4, false), List())), - (81L, MethodRefDefSig(false, false, false, false, 0, Tpe(Void, false), List())) + (72L, MethodRefDefSig(true, false, false, false, 0, Tpe(I4, false), List())), + (76L, MethodRefDefSig(false, false, false, false, 0, Tpe(Void, false), List())) ) diff --git a/dotnet/src/test/resources/parser/ProgramInterface.prs b/dotnet/src/test/resources/parser/ProgramInterface.prs new file mode 100644 index 00000000..ad6b1295 --- /dev/null +++ b/dotnet/src/test/resources/parser/ProgramInterface.prs @@ -0,0 +1,92 @@ +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: ProgramInterface.dll + sources: + - Pravda.dll + - dotnet-tests/resources/ProgramInterface.cs + optimize: true + - target: ProgramInterfaceCheck.exe + sources: + - Pravda.dll + - ProgramInterface.dll + - dotnet-tests/resources/ProgramInterfaceCheck.cs + optimize: true + main-class: InterfaceNamespace.ProgramInterfaceCheck +--- +methods: |- + List( + Method( + List( + LdStr("1eaed20b7ce2b336043e4b340b031f95bb1ce6d935ef733ae4df1b66e1e3d91f"), + NewObj(MemberRefData(TypeRefData(10L, "Bytes", "Expload.Pravda"), ".ctor", 16L)), + Call( + MethodSpecData( + MemberRefData(TypeRefData(10L, "ProgramHelper", "Expload.Pravda"), "Program", 21L), + 29L + ) + ), + LdcI41, + LdcI42, + CallVirt( + MemberRefData(TypeRefData(14L, "ProgramInterface", "InterfaceNamespace"), "Add", 34L) + ), + Ret + ), + 0, + None + ), + Method(List(Ret), 0, None), + Method( + List(LdArg0, Call(MemberRefData(TypeRefData(6L, "Object", "System"), ".ctor", 6L)), Ret), + 0, + None + ) + ) +signatures: |- + List( + (1L, MethodRefDefSig(true, false, false, false, 0, Tpe(Void, false), List(Tpe(I4, false)))), + (6L, MethodRefDefSig(true, false, false, false, 0, Tpe(Void, false), List())), + ( + 10L, + MethodRefDefSig( + true, + false, + false, + false, + 0, + Tpe(Void, false), + List(Tpe(ValueTpe(TypeRefData(15L, "DebuggingModes", "")), false)) + ) + ), + (16L, MethodRefDefSig(true, false, false, false, 0, Tpe(Void, false), List(Tpe(String, false)))), + ( + 21L, + MethodRefDefSig( + false, + false, + false, + false, + 1, + Tpe(MVar(0), false), + List(Tpe(Cls(TypeRefData(10L, "Bytes", "Expload.Pravda")), false)) + ) + ), + ( + 34L, + MethodRefDefSig( + true, + false, + false, + false, + 0, + Tpe(I4, false), + List(Tpe(I4, false), Tpe(I4, false)) + ) + ), + (49L, MethodRefDefSig(true, false, false, false, 0, Tpe(I4, false), List())), + (53L, MethodRefDefSig(false, false, false, false, 0, Tpe(Void, false), List())) + ) diff --git a/dotnet/src/test/resources/parser/smart_program.prs b/dotnet/src/test/resources/parser/SmartProgram.prs similarity index 60% rename from dotnet/src/test/resources/parser/smart_program.prs rename to dotnet/src/test/resources/parser/SmartProgram.prs index 8dff14c1..dcc08508 100644 --- a/dotnet/src/test/resources/parser/smart_program.prs +++ b/dotnet/src/test/resources/parser/SmartProgram.prs @@ -1,86 +1,97 @@ -exe: smart_program.exe +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: SmartProgram.exe + sources: + - Pravda.dll + - dotnet-tests/resources/SmartProgram.cs + optimize: true --- methods: |- List( Method( List( - Nop, LdArg0, - LdFld(FieldData(1, "balances", 64L)), + LdFld(FieldData(0, 1, "Balances", 55L)), LdArg1, LdcI40, - CallVirt(MemberRefData(TypeSpecData(20L), "getDefault", 28L)), - StLoc0, - BrS(0), - LdLoc0, + CallVirt(MemberRefData(TypeSpecData(16L), "GetOrDefault", 24L)), Ret ), - 3, - Some(16L) + 0, + None ), Method( List( - Nop, LdArg2, LdcI40, - Cgt, - StLoc0, - LdLoc0, - BrFalseS(95), - Nop, + BleS(82), LdArg0, - LdFld(FieldData(1, "balances", 64L)), - Call(MemberRefData(TypeRefData(10L, "Info", "Expload.Pravda"), "Sender", 42L)), + LdFld(FieldData(0, 1, "Balances", 55L)), + Call(MemberRefData(TypeRefData(10L, "Info", "Expload.Pravda"), "Sender", 33L)), LdcI40, - CallVirt(MemberRefData(TypeSpecData(20L), "getDefault", 28L)), + CallVirt(MemberRefData(TypeSpecData(16L), "GetOrDefault", 24L)), LdArg2, - Clt, - LdcI40, - Ceq, - StLoc1, - LdLoc1, - BrFalseS(66), - Nop, + BltS(62), LdArg0, - LdFld(FieldData(1, "balances", 64L)), - Call(MemberRefData(TypeRefData(10L, "Info", "Expload.Pravda"), "Sender", 42L)), + LdFld(FieldData(0, 1, "Balances", 55L)), + Call(MemberRefData(TypeRefData(10L, "Info", "Expload.Pravda"), "Sender", 33L)), LdArg0, - LdFld(FieldData(1, "balances", 64L)), - Call(MemberRefData(TypeRefData(10L, "Info", "Expload.Pravda"), "Sender", 42L)), + LdFld(FieldData(0, 1, "Balances", 55L)), + Call(MemberRefData(TypeRefData(10L, "Info", "Expload.Pravda"), "Sender", 33L)), LdcI40, - CallVirt(MemberRefData(TypeSpecData(20L), "getDefault", 28L)), + CallVirt(MemberRefData(TypeSpecData(16L), "GetOrDefault", 24L)), LdArg2, Sub, - CallVirt(MemberRefData(TypeSpecData(20L), "put", 47L)), - Nop, + CallVirt(MemberRefData(TypeSpecData(16L), "set_Item", 38L)), LdArg0, - LdFld(FieldData(1, "balances", 64L)), + LdFld(FieldData(0, 1, "Balances", 55L)), LdArg1, LdArg0, - LdFld(FieldData(1, "balances", 64L)), + LdFld(FieldData(0, 1, "Balances", 55L)), LdArg1, LdcI40, - CallVirt(MemberRefData(TypeSpecData(20L), "getDefault", 28L)), + CallVirt(MemberRefData(TypeSpecData(16L), "GetOrDefault", 24L)), LdArg2, Add, - CallVirt(MemberRefData(TypeSpecData(20L), "put", 47L)), - Nop, - Nop, - Nop, + CallVirt(MemberRefData(TypeSpecData(16L), "set_Item", 38L)), Ret ), 5, - Some(37L) + None + ), + Method( + List( + LdArg2, + LdcI40, + BleS(27), + LdArg0, + LdFld(FieldData(0, 1, "Balances", 55L)), + LdArg1, + LdArg0, + LdFld(FieldData(0, 1, "Balances", 55L)), + LdArg1, + LdcI40, + CallVirt(MemberRefData(TypeSpecData(16L), "GetOrDefault", 24L)), + LdArg2, + Add, + CallVirt(MemberRefData(TypeSpecData(16L), "set_Item", 38L)), + Ret + ), + 0, + None ), - Method(List(Nop, Ret), 0, None), + Method(List(Ret), 0, None), Method( List( LdArg0, - NewObj(MemberRefData(TypeSpecData(20L), ".ctor", 6L)), - StFld(FieldData(1, "balances", 64L)), + NewObj(MemberRefData(TypeSpecData(16L), ".ctor", 6L)), + StFld(FieldData(0, 1, "Balances", 55L)), LdArg0, Call(MemberRefData(TypeRefData(6L, "Object", "System"), ".ctor", 6L)), - Nop, Ret ), 0, @@ -103,9 +114,8 @@ signatures: |- List(Tpe(ValueTpe(TypeRefData(15L, "DebuggingModes", "")), false)) ) ), - (16L, LocalVarSig(List(LocalVar(I4, false)))), ( - 20L, + 16L, TypeSig( Tpe( Generic( @@ -117,7 +127,7 @@ signatures: |- ) ), ( - 28L, + 24L, MethodRefDefSig( true, false, @@ -128,9 +138,8 @@ signatures: |- List(Tpe(Var(0), false), Tpe(Var(1), false)) ) ), - (37L, LocalVarSig(List(LocalVar(Boolean, false), LocalVar(Boolean, false)))), ( - 42L, + 33L, MethodRefDefSig( false, false, @@ -142,7 +151,7 @@ signatures: |- ) ), ( - 47L, + 38L, MethodRefDefSig( true, false, @@ -154,7 +163,7 @@ signatures: |- ) ), ( - 64L, + 55L, FieldSig( Generic( Cls(TypeRefData(10L, "Mapping`2", "Expload.Pravda")), @@ -163,7 +172,7 @@ signatures: |- ) ), ( - 73L, + 64L, MethodRefDefSig( true, false, @@ -175,7 +184,7 @@ signatures: |- ) ), ( - 79L, + 70L, MethodRefDefSig( true, false, @@ -186,5 +195,5 @@ signatures: |- List(Tpe(Cls(TypeRefData(10L, "Bytes", "Expload.Pravda")), false), Tpe(I4, false)) ) ), - (86L, MethodRefDefSig(false, false, false, false, 0, Tpe(Void, false), List())) + (77L, MethodRefDefSig(false, false, false, false, 0, Tpe(Void, false), List())) ) diff --git a/dotnet/src/test/resources/parser/static_class.prs b/dotnet/src/test/resources/parser/StaticClass.prs similarity index 89% rename from dotnet/src/test/resources/parser/static_class.prs rename to dotnet/src/test/resources/parser/StaticClass.prs index 20966539..6d5cbcf8 100644 --- a/dotnet/src/test/resources/parser/static_class.prs +++ b/dotnet/src/test/resources/parser/StaticClass.prs @@ -1,11 +1,21 @@ -exe: static_class.exe +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: StaticClass.exe + sources: + - Pravda.dll + - dotnet-tests/resources/StaticClass.cs + optimize: true --- methods: |- List( Method( List( LdArg1, - Call(MethodDefData(5, 0, 150, "BytesToHex", 65L, Vector(ParamData(0, 1, "bytes")))), + Call(MethodDefData(5, 0, 150, "BytesToHex", 60L, Vector(ParamData(0, 1, "bytes")))), Ret ), 0, @@ -126,12 +136,14 @@ methods: |- StLoc0, LdcI40, StLoc1, - BrS(23), + BrS(29), LdLoc0, LdArg0, LdLoc1, CallVirt(MemberRefData(TypeRefData(10L, "Bytes", "Expload.Pravda"), "get_Item", 27L)), - Call(MethodDefData(4, 0, 150, "ByteToHex", 60L, Vector(ParamData(0, 1, "b")))), + LdcI4(255), + And, + Call(MethodDefData(4, 0, 150, "ByteToHex", 55L, Vector(ParamData(0, 1, "b")))), Call(MemberRefData(TypeRefData(6L, "String", "System"), "Concat", 16L)), StLoc0, LdLoc1, @@ -141,7 +153,7 @@ methods: |- LdLoc1, LdArg0, CallVirt(MemberRefData(TypeRefData(10L, "Bytes", "Expload.Pravda"), "Length", 32L)), - BltS(-32), + BltS(-38), LdLoc0, Ret ), @@ -178,7 +190,7 @@ signatures: |- ) ), (22L, LocalVarSig(List(LocalVar(String, false), LocalVar(I4, false)))), - (27L, MethodRefDefSig(true, false, false, false, 0, Tpe(U1, false), List(Tpe(I4, false)))), + (27L, MethodRefDefSig(true, false, false, false, 0, Tpe(I1, false), List(Tpe(I4, false)))), (32L, MethodRefDefSig(true, false, false, false, 0, Tpe(I4, false), List())), ( 45L, @@ -194,9 +206,8 @@ signatures: |- ), (51L, MethodRefDefSig(false, false, false, false, 0, Tpe(Void, false), List())), (55L, MethodRefDefSig(false, false, false, false, 0, Tpe(String, false), List(Tpe(I4, false)))), - (60L, MethodRefDefSig(false, false, false, false, 0, Tpe(String, false), List(Tpe(U1, false)))), ( - 65L, + 60L, MethodRefDefSig( false, false, diff --git a/dotnet/src/test/resources/parser/Stdlib.prs b/dotnet/src/test/resources/parser/Stdlib.prs new file mode 100644 index 00000000..d87679e5 --- /dev/null +++ b/dotnet/src/test/resources/parser/Stdlib.prs @@ -0,0 +1,150 @@ +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: Stdlib.exe + sources: + - Pravda.dll + - dotnet-tests/resources/Stdlib.cs + optimize: true +--- +methods: |- + List( + Method( + List( + LdArg1, + Call(MemberRefData(TypeRefData(10L, "StdLib", "Expload.Pravda"), "Ripemd160", 16L)), + Ret + ), + 0, + None + ), + Method( + List( + LdArg1, + LdArg2, + LdArg3, + Call( + MemberRefData(TypeRefData(10L, "StdLib", "Expload.Pravda"), "ValidateEd25519Signature", 22L) + ), + Ret + ), + 0, + None + ), + Method( + List( + LdArg1, + Call(MemberRefData(TypeRefData(10L, "StdLib", "Expload.Pravda"), "BytesToHex", 31L)), + Ret + ), + 0, + None + ), + Method(List(Ret), 0, None), + Method( + List(LdArg0, Call(MemberRefData(TypeRefData(6L, "Object", "System"), ".ctor", 6L)), Ret), + 0, + None + ) + ) +signatures: |- + List( + (1L, MethodRefDefSig(true, false, false, false, 0, Tpe(Void, false), List(Tpe(I4, false)))), + (6L, MethodRefDefSig(true, false, false, false, 0, Tpe(Void, false), List())), + ( + 10L, + MethodRefDefSig( + true, + false, + false, + false, + 0, + Tpe(Void, false), + List(Tpe(ValueTpe(TypeRefData(15L, "DebuggingModes", "")), false)) + ) + ), + ( + 16L, + MethodRefDefSig( + false, + false, + false, + false, + 0, + Tpe(Cls(TypeRefData(10L, "Bytes", "Expload.Pravda")), false), + List(Tpe(String, false)) + ) + ), + ( + 22L, + MethodRefDefSig( + false, + false, + false, + false, + 0, + Tpe(Boolean, false), + List( + Tpe(Cls(TypeRefData(10L, "Bytes", "Expload.Pravda")), false), + Tpe(String, false), + Tpe(Cls(TypeRefData(10L, "Bytes", "Expload.Pravda")), false) + ) + ) + ), + ( + 31L, + MethodRefDefSig( + false, + false, + false, + false, + 0, + Tpe(String, false), + List(Tpe(Cls(TypeRefData(10L, "Bytes", "Expload.Pravda")), false)) + ) + ), + ( + 46L, + MethodRefDefSig( + true, + false, + false, + false, + 0, + Tpe(Cls(TypeRefData(10L, "Bytes", "Expload.Pravda")), false), + List(Tpe(String, false)) + ) + ), + ( + 52L, + MethodRefDefSig( + true, + false, + false, + false, + 0, + Tpe(Boolean, false), + List( + Tpe(Cls(TypeRefData(10L, "Bytes", "Expload.Pravda")), false), + Tpe(String, false), + Tpe(Cls(TypeRefData(10L, "Bytes", "Expload.Pravda")), false) + ) + ) + ), + ( + 61L, + MethodRefDefSig( + true, + false, + false, + false, + 0, + Tpe(String, false), + List(Tpe(Cls(TypeRefData(10L, "Bytes", "Expload.Pravda")), false)) + ) + ), + (67L, MethodRefDefSig(false, false, false, false, 0, Tpe(Void, false), List())) + ) diff --git a/dotnet/src/test/resources/parser/strings.prs b/dotnet/src/test/resources/parser/Strings.prs similarity index 63% rename from dotnet/src/test/resources/parser/strings.prs rename to dotnet/src/test/resources/parser/Strings.prs index a02c4c9e..2b9e4938 100644 --- a/dotnet/src/test/resources/parser/strings.prs +++ b/dotnet/src/test/resources/parser/Strings.prs @@ -1,68 +1,68 @@ -exe: strings.exe +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: Strings.exe + sources: + - Pravda.dll + - dotnet-tests/resources/Strings.cs + optimize: true --- methods: |- List( Method( List( - Nop, LdStr("zauser1"), StLoc0, LdStr("us"), - StLoc1, LdStr("er2"), - StLoc2, + StLoc1, LdLoc1, - LdLoc2, - Call(MemberRefData(TypeRefData(6L, "String", "System"), "Concat", 27L)), - StLoc3, + Call(MemberRefData(TypeRefData(6L, "String", "System"), "Concat", 22L)), + StLoc2, LdArg0, - LdFld(FieldData(6, "strings", 74L)), - LdLoc3, + LdFld(FieldData(0, 1, "StringsMapping", 69L)), + LdLoc2, LdLoc0, - CallVirt(MemberRefData(TypeSpecData(33L), "put", 40L)), - Nop, + CallVirt(MemberRefData(TypeSpecData(28L), "set_Item", 35L)), LdArg0, - LdFld(FieldData(6, "strings", 74L)), + LdFld(FieldData(0, 1, "StringsMapping", 69L)), LdStr("user1"), - CallVirt(MemberRefData(TypeSpecData(33L), "exists", 48L)), - StLocS(7), - LdLocS(7), - BrFalseS(24), - Nop, + CallVirt(MemberRefData(TypeSpecData(28L), "ContainsKey", 43L)), + BrFalseS(21), LdArg0, - LdFld(FieldData(6, "strings", 74L)), + LdFld(FieldData(0, 1, "StringsMapping", 69L)), LdStr("user2"), LdStr(""), - CallVirt(MemberRefData(TypeSpecData(33L), "put", 40L)), - Nop, - Nop, + CallVirt(MemberRefData(TypeSpecData(28L), "set_Item", 35L)), LdLoc0, LdcI40, - CallVirt(MemberRefData(TypeRefData(6L, "String", "System"), "get_Chars", 54L)), - StLocS(4), - LdLoc3, + CallVirt(MemberRefData(TypeRefData(6L, "String", "System"), "get_Chars", 49L)), + Pop, + LdLoc2, LdcI43, - CallVirt(MemberRefData(TypeRefData(6L, "String", "System"), "get_Chars", 54L)), - StLocS(5), - LdLoc3, + CallVirt(MemberRefData(TypeRefData(6L, "String", "System"), "get_Chars", 49L)), + Pop, + LdLoc2, LdcI41, LdcI42, - CallVirt(MemberRefData(TypeRefData(6L, "String", "System"), "Substring", 59L)), - StLocS(6), + CallVirt(MemberRefData(TypeRefData(6L, "String", "System"), "Substring", 54L)), + Pop, Ret ), 3, Some(16L) ), - Method(List(Nop, Ret), 0, None), + Method(List(Ret), 0, None), Method( List( LdArg0, - NewObj(MemberRefData(TypeSpecData(33L), ".ctor", 6L)), - StFld(FieldData(6, "strings", 74L)), + NewObj(MemberRefData(TypeSpecData(28L), ".ctor", 6L)), + StFld(FieldData(0, 1, "StringsMapping", 69L)), LdArg0, Call(MemberRefData(TypeRefData(6L, "Object", "System"), ".ctor", 6L)), - Nop, Ret ), 0, @@ -87,21 +87,10 @@ signatures: |- ), ( 16L, - LocalVarSig( - List( - LocalVar(String, false), - LocalVar(String, false), - LocalVar(String, false), - LocalVar(String, false), - LocalVar(Char, false), - LocalVar(Char, false), - LocalVar(String, false), - LocalVar(Boolean, false) - ) - ) + LocalVarSig(List(LocalVar(String, false), LocalVar(String, false), LocalVar(String, false))) ), ( - 27L, + 22L, MethodRefDefSig( false, false, @@ -113,7 +102,7 @@ signatures: |- ) ), ( - 33L, + 28L, TypeSig( Tpe( Generic(Cls(TypeRefData(10L, "Mapping`2", "Expload.Pravda")), List(String, String)), @@ -122,7 +111,7 @@ signatures: |- ) ), ( - 40L, + 35L, MethodRefDefSig( true, false, @@ -134,12 +123,12 @@ signatures: |- ) ), ( - 48L, + 43L, MethodRefDefSig(true, false, false, false, 0, Tpe(Boolean, false), List(Tpe(Var(0), false))) ), - (54L, MethodRefDefSig(true, false, false, false, 0, Tpe(Char, false), List(Tpe(I4, false)))), + (49L, MethodRefDefSig(true, false, false, false, 0, Tpe(Char, false), List(Tpe(I4, false)))), ( - 59L, + 54L, MethodRefDefSig( true, false, @@ -151,8 +140,8 @@ signatures: |- ) ), ( - 74L, + 69L, FieldSig(Generic(Cls(TypeRefData(10L, "Mapping`2", "Expload.Pravda")), List(String, String))) ), - (82L, MethodRefDefSig(false, false, false, false, 0, Tpe(Void, false), List())) + (77L, MethodRefDefSig(false, false, false, false, 0, Tpe(Void, false), List())) ) diff --git a/dotnet/src/test/resources/parser/SystemMethods.prs b/dotnet/src/test/resources/parser/SystemMethods.prs new file mode 100644 index 00000000..cf98a721 --- /dev/null +++ b/dotnet/src/test/resources/parser/SystemMethods.prs @@ -0,0 +1,96 @@ +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: SystemMethods.exe + sources: + - Pravda.dll + - dotnet-tests/resources/SystemMethods.cs + optimize: true +--- +methods: |- + List( + Method( + List( + LdSFld(MemberRefData(TypeRefData(10L, "Bytes", "Expload.Pravda"), "VOID_ADDRESS", 16L)), + Call(MemberRefData(TypeRefData(10L, "Info", "Expload.Pravda"), "Balance", 20L)), + Pop, + Call(MemberRefData(TypeRefData(10L, "Info", "Expload.Pravda"), "ProgramAddress", 26L)), + Pop, + LdSFld(MemberRefData(TypeRefData(10L, "Bytes", "Expload.Pravda"), "VOID_ADDRESS", 16L)), + LdcI4S(100), + ConvI8, + Call(MemberRefData(TypeRefData(10L, "Actions", "Expload.Pravda"), "Transfer", 31L)), + LdSFld(MemberRefData(TypeRefData(10L, "Bytes", "Expload.Pravda"), "VOID_ADDRESS", 16L)), + LdcI4(200), + ConvI8, + Call(MemberRefData(TypeRefData(10L, "Actions", "Expload.Pravda"), "TransferFromProgram", 31L)), + Ret + ), + 0, + None + ), + Method(List(Ret), 0, None), + Method( + List(LdArg0, Call(MemberRefData(TypeRefData(6L, "Object", "System"), ".ctor", 6L)), Ret), + 0, + None + ) + ) +signatures: |- + List( + (1L, MethodRefDefSig(true, false, false, false, 0, Tpe(Void, false), List(Tpe(I4, false)))), + (6L, MethodRefDefSig(true, false, false, false, 0, Tpe(Void, false), List())), + ( + 10L, + MethodRefDefSig( + true, + false, + false, + false, + 0, + Tpe(Void, false), + List(Tpe(ValueTpe(TypeRefData(15L, "DebuggingModes", "")), false)) + ) + ), + (16L, FieldSig(Cls(TypeRefData(10L, "Bytes", "Expload.Pravda")))), + ( + 20L, + MethodRefDefSig( + false, + false, + false, + false, + 0, + Tpe(I8, false), + List(Tpe(Cls(TypeRefData(10L, "Bytes", "Expload.Pravda")), false)) + ) + ), + ( + 26L, + MethodRefDefSig( + false, + false, + false, + false, + 0, + Tpe(Cls(TypeRefData(10L, "Bytes", "Expload.Pravda")), false), + List() + ) + ), + ( + 31L, + MethodRefDefSig( + false, + false, + false, + false, + 0, + Tpe(Void, false), + List(Tpe(Cls(TypeRefData(10L, "Bytes", "Expload.Pravda")), false), Tpe(I8, false)) + ) + ), + (47L, MethodRefDefSig(false, false, false, false, 0, Tpe(Void, false), List())) + ) diff --git a/dotnet/src/test/resources/parser/VmOps.prs b/dotnet/src/test/resources/parser/VmOps.prs new file mode 100644 index 00000000..ccc524b9 --- /dev/null +++ b/dotnet/src/test/resources/parser/VmOps.prs @@ -0,0 +1,49 @@ +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: VmOps.exe + sources: + - Pravda.dll + - dotnet-tests/resources/VmOps.cs + optimize: true +--- +methods: |- + List( + Method( + List( + LdStr("Oops!"), + Call(MemberRefData(TypeRefData(10L, "Error", "Expload.Pravda"), "Throw", 16L)), + Ret + ), + 0, + None + ), + Method(List(Ret), 0, None), + Method( + List(LdArg0, Call(MemberRefData(TypeRefData(6L, "Object", "System"), ".ctor", 6L)), Ret), + 0, + None + ) + ) +signatures: |- + List( + (1L, MethodRefDefSig(true, false, false, false, 0, Tpe(Void, false), List(Tpe(I4, false)))), + (6L, MethodRefDefSig(true, false, false, false, 0, Tpe(Void, false), List())), + ( + 10L, + MethodRefDefSig( + true, + false, + false, + false, + 0, + Tpe(Void, false), + List(Tpe(ValueTpe(TypeRefData(15L, "DebuggingModes", "")), false)) + ) + ), + (16L, MethodRefDefSig(false, false, false, false, 0, Tpe(Void, false), List(Tpe(String, false)))), + (30L, MethodRefDefSig(false, false, false, false, 0, Tpe(Void, false), List())) + ) diff --git a/dotnet/src/test/resources/parser/zoo_program.prs b/dotnet/src/test/resources/parser/ZooProgram.prs similarity index 54% rename from dotnet/src/test/resources/parser/zoo_program.prs rename to dotnet/src/test/resources/parser/ZooProgram.prs index fd1c0dd9..d9acbc28 100644 --- a/dotnet/src/test/resources/parser/zoo_program.prs +++ b/dotnet/src/test/resources/parser/ZooProgram.prs @@ -1,45 +1,46 @@ -exe: zoo_program.exe +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: ZooProgram.exe + sources: + - Pravda.dll + - dotnet-tests/resources/ZooProgram.cs + optimize: true --- methods: |- List( Method( List( - Nop, LdcI4S(10), - NewArr(TypeRefData(6L, "Byte", "System")), + NewArr(TypeRefData(6L, "SByte", "System")), StLoc0, LdcI40, StLoc1, - BrS(30), - Nop, + BrS(28), LdLoc0, LdLoc1, LdArg1, LdLoc1, LdArg1, - CallVirt(MemberRefData(TypeRefData(6L, "String", "System"), "get_Length", 25L)), + CallVirt(MemberRefData(TypeRefData(6L, "String", "System"), "get_Length", 22L)), Rem, - CallVirt(MemberRefData(TypeRefData(6L, "String", "System"), "get_Chars", 29L)), + CallVirt(MemberRefData(TypeRefData(6L, "String", "System"), "get_Chars", 26L)), LdcI42, Div, - Call(MemberRefData(TypeRefData(6L, "Convert", "System"), "ToByte", 34L)), + Call(MemberRefData(TypeRefData(6L, "Convert", "System"), "ToSByte", 31L)), StElemI1, - Nop, LdLoc1, LdcI41, Add, StLoc1, LdLoc1, LdcI4S(10), - Clt, - StLoc2, - LdLoc2, - BrTrueS(-39), + BltS(-33), LdLoc0, - NewObj(MemberRefData(TypeRefData(10L, "Bytes", "Expload.Pravda"), ".ctor", 39L)), - StLoc3, - BrS(0), - LdLoc3, + NewObj(MemberRefData(TypeRefData(10L, "Bytes", "Expload.Pravda"), ".ctor", 36L)), Ret ), 5, @@ -47,246 +48,198 @@ methods: |- ), Method( List( - Nop, LdArg0, - LdFld(FieldData(6, "ZooToOwner", 159L)), + LdFld(FieldData(3, 1, "ZooToOwner", 146L)), LdArg0, - LdFld(FieldData(6, "ZooCnt", 168L)), - Call(MemberRefData(TypeRefData(10L, "Info", "Expload.Pravda"), "Sender", 49L)), - CallVirt(MemberRefData(TypeSpecData(54L), "put", 62L)), - Nop, + LdFld(FieldData(4, 1, "ZooCnt", 155L)), + Call(MemberRefData(TypeRefData(10L, "Info", "Expload.Pravda"), "Sender", 42L)), + CallVirt(MemberRefData(TypeSpecData(47L), "set_Item", 55L)), LdArg0, LdArg0, - LdFld(FieldData(6, "ZooCnt", 168L)), + LdFld(FieldData(4, 1, "ZooCnt", 155L)), LdcI41, Add, - StFld(FieldData(6, "ZooCnt", 168L)), + StFld(FieldData(4, 1, "ZooCnt", 155L)), LdArg0, - LdFld(FieldData(6, "ZooCnt", 168L)), + LdFld(FieldData(4, 1, "ZooCnt", 155L)), LdcI41, Sub, - StLoc0, - BrS(0), - LdLoc0, Ret ), - 3, - Some(45L) + 0, + None ), Method( List( - Nop, LdArg0, - LdFld(FieldData(6, "ZooToOwner", 159L)), + LdFld(FieldData(3, 1, "ZooToOwner", 146L)), LdArg2, - LdSFld(MemberRefData(TypeRefData(10L, "Bytes", "Expload.Pravda"), "EMPTY", 74L)), - CallVirt(MemberRefData(TypeSpecData(54L), "getDefault", 78L)), - Call(MemberRefData(TypeRefData(10L, "Info", "Expload.Pravda"), "Sender", 49L)), - Ceq, - StLoc0, - LdLoc0, - BrFalseS(16), - Nop, + LdSFld(MemberRefData(TypeRefData(10L, "Bytes", "Expload.Pravda"), "EMPTY", 63L)), + CallVirt(MemberRefData(TypeSpecData(47L), "GetOrDefault", 67L)), + Call(MemberRefData(TypeRefData(10L, "Info", "Expload.Pravda"), "Sender", 42L)), + BneUnS(13), LdArg0, - LdFld(FieldData(6, "ZooToOwner", 159L)), + LdFld(FieldData(3, 1, "ZooToOwner", 146L)), LdArg2, LdArg1, - CallVirt(MemberRefData(TypeSpecData(54L), "put", 62L)), - Nop, - Nop, + CallVirt(MemberRefData(TypeSpecData(47L), "set_Item", 55L)), Ret ), - 3, - Some(70L) + 0, + None ), Method( List( - Nop, LdArg0, - LdFld(FieldData(6, "ZooToOwner", 159L)), + LdFld(FieldData(3, 1, "ZooToOwner", 146L)), LdArg1, - LdSFld(MemberRefData(TypeRefData(10L, "Bytes", "Expload.Pravda"), "EMPTY", 74L)), - CallVirt(MemberRefData(TypeSpecData(54L), "getDefault", 78L)), - Call(MemberRefData(TypeRefData(10L, "Info", "Expload.Pravda"), "Sender", 49L)), - Ceq, - StLoc0, - LdLoc0, - BrFalseS(79), - Nop, + LdSFld(MemberRefData(TypeRefData(10L, "Bytes", "Expload.Pravda"), "EMPTY", 63L)), + CallVirt(MemberRefData(TypeSpecData(47L), "GetOrDefault", 67L)), + Call(MemberRefData(TypeRefData(10L, "Info", "Expload.Pravda"), "Sender", 42L)), + BneUnS(74), LdStr("pet"), LdArg0, - LdFld(FieldData(6, "PetId", 168L)), - Call(MemberRefData(TypeRefData(6L, "Convert", "System"), "ToString", 93L)), - Call(MemberRefData(TypeRefData(6L, "String", "System"), "Concat", 98L)), - StLoc1, + LdFld(FieldData(5, 1, "PetId", 155L)), + Call(MemberRefData(TypeRefData(6L, "Convert", "System"), "ToString", 80L)), + Call(MemberRefData(TypeRefData(6L, "String", "System"), "Concat", 85L)), + StLoc0, LdArg0, - LdFld(FieldData(6, "PetToOwner", 150L)), - LdLoc1, - Call(MemberRefData(TypeRefData(10L, "Info", "Expload.Pravda"), "Sender", 49L)), - CallVirt(MemberRefData(TypeSpecData(104L), "put", 62L)), - Nop, + LdFld(FieldData(2, 1, "PetToOwner", 137L)), + LdLoc0, + Call(MemberRefData(TypeRefData(10L, "Info", "Expload.Pravda"), "Sender", 42L)), + CallVirt(MemberRefData(TypeSpecData(91L), "set_Item", 55L)), LdArg0, - LdFld(FieldData(6, "PetSignature", 150L)), - LdLoc1, + LdFld(FieldData(1, 1, "PetSignature", 137L)), + LdLoc0, LdArg0, - LdLoc1, - Call(MethodDefData(0, 0, 129, "GenerateSignature", 171L, Vector(ParamData(0, 1, "pet")))), - CallVirt(MemberRefData(TypeSpecData(104L), "put", 62L)), - Nop, + LdLoc0, + Call(MethodDefData(0, 0, 129, "GenerateSignature", 158L, Vector(ParamData(0, 1, "pet")))), + CallVirt(MemberRefData(TypeSpecData(91L), "set_Item", 55L)), LdArg0, LdArg0, - LdFld(FieldData(6, "PetId", 168L)), + LdFld(FieldData(5, 1, "PetId", 155L)), LdcI41, Add, - StFld(FieldData(6, "PetId", 168L)), - LdLoc1, - StLoc2, - BrS(8), + StFld(FieldData(5, 1, "PetId", 155L)), + LdLoc0, + Ret, LdStr(""), - StLoc2, - BrS(0), - LdLoc2, Ret ), 4, - Some(87L) + Some(76L) ), Method( List( - Nop, LdArg0, - LdFld(FieldData(6, "PetToOwner", 150L)), + LdFld(FieldData(2, 1, "PetToOwner", 137L)), LdArg3, - LdSFld(MemberRefData(TypeRefData(10L, "Bytes", "Expload.Pravda"), "EMPTY", 74L)), - CallVirt(MemberRefData(TypeSpecData(104L), "getDefault", 78L)), - Call(MemberRefData(TypeRefData(10L, "Info", "Expload.Pravda"), "Sender", 49L)), - BneUnS(22), + LdSFld(MemberRefData(TypeRefData(10L, "Bytes", "Expload.Pravda"), "EMPTY", 63L)), + CallVirt(MemberRefData(TypeSpecData(91L), "GetOrDefault", 67L)), + Call(MemberRefData(TypeRefData(10L, "Info", "Expload.Pravda"), "Sender", 42L)), + BneUnS(46), LdArg0, - LdFld(FieldData(6, "ZooToOwner", 159L)), + LdFld(FieldData(3, 1, "ZooToOwner", 146L)), LdArg2, - LdSFld(MemberRefData(TypeRefData(10L, "Bytes", "Expload.Pravda"), "EMPTY", 74L)), - CallVirt(MemberRefData(TypeSpecData(54L), "getDefault", 78L)), + LdSFld(MemberRefData(TypeRefData(10L, "Bytes", "Expload.Pravda"), "EMPTY", 63L)), + CallVirt(MemberRefData(TypeSpecData(47L), "GetOrDefault", 67L)), LdArg1, - Ceq, - BrS(1), - LdcI40, - StLoc0, - LdLoc0, - BrFalseS(30), - Nop, + BneUnS(26), LdArg0, - LdFld(FieldData(6, "PetToOwner", 150L)), + LdFld(FieldData(2, 1, "PetToOwner", 137L)), LdArg3, LdArg1, - CallVirt(MemberRefData(TypeSpecData(104L), "put", 62L)), - Nop, + CallVirt(MemberRefData(TypeSpecData(91L), "set_Item", 55L)), LdArg0, - LdFld(FieldData(6, "PetToZoo", 142L)), + LdFld(FieldData(0, 1, "PetToZoo", 129L)), LdArg3, LdArg2, - CallVirt(MemberRefData(TypeSpecData(112L), "put", 62L)), - Nop, - Nop, + CallVirt(MemberRefData(TypeSpecData(99L), "set_Item", 55L)), Ret ), 3, - Some(70L) + None ), Method( List( - Nop, LdArg0, - LdFld(FieldData(6, "PetToOwner", 150L)), + LdFld(FieldData(2, 1, "PetToOwner", 137L)), LdArg1, - LdSFld(MemberRefData(TypeRefData(10L, "Bytes", "Expload.Pravda"), "EMPTY", 74L)), - CallVirt(MemberRefData(TypeSpecData(104L), "getDefault", 78L)), - Call(MemberRefData(TypeRefData(10L, "Info", "Expload.Pravda"), "Sender", 49L)), - BneUnS(54), + LdSFld(MemberRefData(TypeRefData(10L, "Bytes", "Expload.Pravda"), "EMPTY", 63L)), + CallVirt(MemberRefData(TypeSpecData(91L), "GetOrDefault", 67L)), + Call(MemberRefData(TypeRefData(10L, "Info", "Expload.Pravda"), "Sender", 42L)), + BneUnS(120), LdArg0, - LdFld(FieldData(6, "PetToOwner", 150L)), + LdFld(FieldData(2, 1, "PetToOwner", 137L)), LdArg2, - LdSFld(MemberRefData(TypeRefData(10L, "Bytes", "Expload.Pravda"), "EMPTY", 74L)), - CallVirt(MemberRefData(TypeSpecData(104L), "getDefault", 78L)), - Call(MemberRefData(TypeRefData(10L, "Info", "Expload.Pravda"), "Sender", 49L)), - BneUnS(30), + LdSFld(MemberRefData(TypeRefData(10L, "Bytes", "Expload.Pravda"), "EMPTY", 63L)), + CallVirt(MemberRefData(TypeSpecData(91L), "GetOrDefault", 67L)), + Call(MemberRefData(TypeRefData(10L, "Info", "Expload.Pravda"), "Sender", 42L)), + BneUnS(96), LdArg0, - LdFld(FieldData(6, "PetToZoo", 142L)), + LdFld(FieldData(0, 1, "PetToZoo", 129L)), LdArg1, LdcI4M1, - CallVirt(MemberRefData(TypeSpecData(112L), "getDefault", 78L)), + CallVirt(MemberRefData(TypeSpecData(99L), "GetOrDefault", 67L)), LdArg0, - LdFld(FieldData(6, "PetToZoo", 142L)), + LdFld(FieldData(0, 1, "PetToZoo", 129L)), LdArg2, LdcI4M1, - CallVirt(MemberRefData(TypeSpecData(112L), "getDefault", 78L)), - Ceq, - BrS(1), - LdcI40, - StLoc0, - LdLoc0, - BrFalseS(73), - Nop, + CallVirt(MemberRefData(TypeSpecData(99L), "GetOrDefault", 67L)), + BneUnS(68), LdArg1, LdArg2, - Call(MemberRefData(TypeRefData(6L, "String", "System"), "Concat", 98L)), - StLoc1, + Call(MemberRefData(TypeRefData(6L, "String", "System"), "Concat", 85L)), + StLoc0, LdArg0, - LdFld(FieldData(6, "PetToOwner", 150L)), - LdLoc1, - Call(MemberRefData(TypeRefData(10L, "Info", "Expload.Pravda"), "Sender", 49L)), - CallVirt(MemberRefData(TypeSpecData(104L), "put", 62L)), - Nop, + LdFld(FieldData(2, 1, "PetToOwner", 137L)), + LdLoc0, + Call(MemberRefData(TypeRefData(10L, "Info", "Expload.Pravda"), "Sender", 42L)), + CallVirt(MemberRefData(TypeSpecData(91L), "set_Item", 55L)), LdArg0, - LdFld(FieldData(6, "PetSignature", 150L)), - LdLoc1, + LdFld(FieldData(1, 1, "PetSignature", 137L)), + LdLoc0, LdArg0, - LdFld(FieldData(6, "PetSignature", 150L)), + LdFld(FieldData(1, 1, "PetSignature", 137L)), LdArg1, - CallVirt(MemberRefData(TypeSpecData(104L), "get", 119L)), + CallVirt(MemberRefData(TypeSpecData(91L), "get_Item", 106L)), LdArg0, - LdFld(FieldData(6, "PetSignature", 150L)), + LdFld(FieldData(1, 1, "PetSignature", 137L)), LdArg2, - CallVirt(MemberRefData(TypeSpecData(104L), "get", 119L)), - CallVirt(MemberRefData(TypeRefData(10L, "Bytes", "Expload.Pravda"), "Concat", 126L)), - CallVirt(MemberRefData(TypeSpecData(104L), "put", 62L)), - Nop, - LdLoc1, - StLoc2, - BrS(9), - Nop, + CallVirt(MemberRefData(TypeSpecData(91L), "get_Item", 106L)), + CallVirt(MemberRefData(TypeRefData(10L, "Bytes", "Expload.Pravda"), "Concat", 113L)), + CallVirt(MemberRefData(TypeSpecData(91L), "set_Item", 55L)), + LdLoc0, + Ret, LdStr(""), - StLoc2, - BrS(0), - LdLoc2, Ret ), 5, - Some(87L) + Some(76L) ), - Method(List(Nop, Ret), 0, None), + Method(List(Ret), 0, None), Method( List( LdArg0, - NewObj(MemberRefData(TypeSpecData(112L), ".ctor", 6L)), - StFld(FieldData(6, "PetToZoo", 142L)), + NewObj(MemberRefData(TypeSpecData(99L), ".ctor", 6L)), + StFld(FieldData(0, 1, "PetToZoo", 129L)), LdArg0, - NewObj(MemberRefData(TypeSpecData(104L), ".ctor", 6L)), - StFld(FieldData(6, "PetSignature", 150L)), + NewObj(MemberRefData(TypeSpecData(91L), ".ctor", 6L)), + StFld(FieldData(1, 1, "PetSignature", 137L)), LdArg0, - NewObj(MemberRefData(TypeSpecData(104L), ".ctor", 6L)), - StFld(FieldData(6, "PetToOwner", 150L)), + NewObj(MemberRefData(TypeSpecData(91L), ".ctor", 6L)), + StFld(FieldData(2, 1, "PetToOwner", 137L)), LdArg0, - NewObj(MemberRefData(TypeSpecData(54L), ".ctor", 6L)), - StFld(FieldData(6, "ZooToOwner", 159L)), + NewObj(MemberRefData(TypeSpecData(47L), ".ctor", 6L)), + StFld(FieldData(3, 1, "ZooToOwner", 146L)), LdArg0, LdcI41, - StFld(FieldData(6, "ZooCnt", 168L)), + StFld(FieldData(4, 1, "ZooCnt", 155L)), LdArg0, LdcI41, - StFld(FieldData(6, "PetId", 168L)), + StFld(FieldData(5, 1, "PetId", 155L)), LdArg0, Call(MemberRefData(TypeRefData(6L, "Object", "System"), ".ctor", 6L)), - Nop, Ret ), 2, @@ -311,20 +264,13 @@ signatures: |- ), ( 16L, - LocalVarSig( - List( - LocalVar(Arr(U1, ArrayShape(1, List(), List())), false), - LocalVar(I4, false), - LocalVar(Boolean, false), - LocalVar(Cls(TypeRefData(10L, "Bytes", "Expload.Pravda")), false) - ) - ) + LocalVarSig(List(LocalVar(Arr(I1, ArrayShape(1, List(), List())), false), LocalVar(I4, false))) ), - (25L, MethodRefDefSig(true, false, false, false, 0, Tpe(I4, false), List())), - (29L, MethodRefDefSig(true, false, false, false, 0, Tpe(Char, false), List(Tpe(I4, false)))), - (34L, MethodRefDefSig(false, false, false, false, 0, Tpe(U1, false), List(Tpe(I4, false)))), + (22L, MethodRefDefSig(true, false, false, false, 0, Tpe(I4, false), List())), + (26L, MethodRefDefSig(true, false, false, false, 0, Tpe(Char, false), List(Tpe(I4, false)))), + (31L, MethodRefDefSig(false, false, false, false, 0, Tpe(I1, false), List(Tpe(I4, false)))), ( - 39L, + 36L, MethodRefDefSig( true, false, @@ -332,12 +278,11 @@ signatures: |- false, 0, Tpe(Void, false), - List(Tpe(Arr(U1, ArrayShape(1, List(), List())), false)) + List(Tpe(Arr(I1, ArrayShape(1, List(), List())), false)) ) ), - (45L, LocalVarSig(List(LocalVar(I4, false)))), ( - 49L, + 42L, MethodRefDefSig( false, false, @@ -349,7 +294,7 @@ signatures: |- ) ), ( - 54L, + 47L, TypeSig( Tpe( Generic( @@ -361,7 +306,7 @@ signatures: |- ) ), ( - 62L, + 55L, MethodRefDefSig( true, false, @@ -372,10 +317,9 @@ signatures: |- List(Tpe(Var(0), false), Tpe(Var(1), false)) ) ), - (70L, LocalVarSig(List(LocalVar(Boolean, false)))), - (74L, FieldSig(Cls(TypeRefData(10L, "Bytes", "Expload.Pravda")))), + (63L, FieldSig(Cls(TypeRefData(10L, "Bytes", "Expload.Pravda")))), ( - 78L, + 67L, MethodRefDefSig( true, false, @@ -386,13 +330,10 @@ signatures: |- List(Tpe(Var(0), false), Tpe(Var(1), false)) ) ), + (76L, LocalVarSig(List(LocalVar(String, false)))), + (80L, MethodRefDefSig(false, false, false, false, 0, Tpe(String, false), List(Tpe(I4, false)))), ( - 87L, - LocalVarSig(List(LocalVar(Boolean, false), LocalVar(String, false), LocalVar(String, false))) - ), - (93L, MethodRefDefSig(false, false, false, false, 0, Tpe(String, false), List(Tpe(I4, false)))), - ( - 98L, + 85L, MethodRefDefSig( false, false, @@ -404,7 +345,7 @@ signatures: |- ) ), ( - 104L, + 91L, TypeSig( Tpe( Generic( @@ -416,17 +357,17 @@ signatures: |- ) ), ( - 112L, + 99L, TypeSig( Tpe(Generic(Cls(TypeRefData(10L, "Mapping`2", "Expload.Pravda")), List(String, I4)), false) ) ), ( - 119L, + 106L, MethodRefDefSig(true, false, false, false, 0, Tpe(Var(1), false), List(Tpe(Var(0), false))) ), ( - 126L, + 113L, MethodRefDefSig( true, false, @@ -437,9 +378,9 @@ signatures: |- List(Tpe(Cls(TypeRefData(10L, "Bytes", "Expload.Pravda")), false)) ) ), - (142L, FieldSig(Generic(Cls(TypeRefData(10L, "Mapping`2", "Expload.Pravda")), List(String, I4)))), + (129L, FieldSig(Generic(Cls(TypeRefData(10L, "Mapping`2", "Expload.Pravda")), List(String, I4)))), ( - 150L, + 137L, FieldSig( Generic( Cls(TypeRefData(10L, "Mapping`2", "Expload.Pravda")), @@ -448,7 +389,7 @@ signatures: |- ) ), ( - 159L, + 146L, FieldSig( Generic( Cls(TypeRefData(10L, "Mapping`2", "Expload.Pravda")), @@ -456,9 +397,9 @@ signatures: |- ) ) ), - (168L, FieldSig(I4)), + (155L, FieldSig(I4)), ( - 171L, + 158L, MethodRefDefSig( true, false, @@ -470,7 +411,7 @@ signatures: |- ) ), ( - 177L, + 164L, MethodRefDefSig( true, false, @@ -481,9 +422,9 @@ signatures: |- List(Tpe(Cls(TypeRefData(10L, "Bytes", "Expload.Pravda")), false), Tpe(I4, false)) ) ), - (184L, MethodRefDefSig(true, false, false, false, 0, Tpe(String, false), List(Tpe(I4, false)))), + (171L, MethodRefDefSig(true, false, false, false, 0, Tpe(String, false), List(Tpe(I4, false)))), ( - 189L, + 176L, MethodRefDefSig( true, false, @@ -499,7 +440,7 @@ signatures: |- ) ), ( - 197L, + 184L, MethodRefDefSig( true, false, @@ -510,5 +451,5 @@ signatures: |- List(Tpe(String, false), Tpe(String, false)) ) ), - (203L, MethodRefDefSig(false, false, false, false, 0, Tpe(Void, false), List())) + (190L, MethodRefDefSig(false, false, false, false, 0, Tpe(Void, false), List())) ) diff --git a/dotnet/src/test/resources/parser/arithmetics.prs b/dotnet/src/test/resources/parser/arithmetics.prs deleted file mode 100644 index b4a6a367..00000000 --- a/dotnet/src/test/resources/parser/arithmetics.prs +++ /dev/null @@ -1,78 +0,0 @@ -exe: arithmetics.exe ---- -methods: |- - List( - Method( - List( - Nop, - LdSFld(FieldData(22, "x", 33L)), - LdcI42, - Add, - StLoc0, - LdSFld(FieldData(22, "x", 33L)), - LdcI42, - Mul, - StLoc1, - LdSFld(FieldData(22, "x", 33L)), - LdcI42, - Div, - StLoc2, - LdSFld(FieldData(22, "x", 33L)), - LdcI42, - Rem, - StLoc3, - LdLoc0, - LdLoc1, - Add, - LdcI4S(42), - Add, - LdLoc2, - Mul, - LdLoc3, - Add, - LdcI4(1337), - Div, - StLocS(4), - Ret - ), - 2, - Some(16L) - ), - Method( - List(LdArg0, Call(MemberRefData(TypeRefData(6L, "Object", "System"), ".ctor", 6L)), Nop, Ret), - 0, - None - ), - Method(List(LdcI4S(10), StSFld(FieldData(22, "x", 33L)), Ret), 0, None) - ) -signatures: |- - List( - (1L, MethodRefDefSig(true, false, false, false, 0, Tpe(Void, false), List(Tpe(I4, false)))), - (6L, MethodRefDefSig(true, false, false, false, 0, Tpe(Void, false), List())), - ( - 10L, - MethodRefDefSig( - true, - false, - false, - false, - 0, - Tpe(Void, false), - List(Tpe(ValueTpe(TypeRefData(15L, "DebuggingModes", "")), false)) - ) - ), - ( - 16L, - LocalVarSig( - List( - LocalVar(I4, false), - LocalVar(I4, false), - LocalVar(I4, false), - LocalVar(I4, false), - LocalVar(I4, false) - ) - ) - ), - (33L, FieldSig(I4)), - (36L, MethodRefDefSig(false, false, false, false, 0, Tpe(Void, false), List())) - ) diff --git a/dotnet/src/test/resources/parser/closure.prs b/dotnet/src/test/resources/parser/closure.prs deleted file mode 100644 index f6367ed7..00000000 --- a/dotnet/src/test/resources/parser/closure.prs +++ /dev/null @@ -1,97 +0,0 @@ -exe: closure.exe ---- -methods: |- - List( - Method( - List( - NewObj(MethodDefData(2, 0, 6278, ".ctor", 6L, Vector())), - StLoc0, - Nop, - LdLoc0, - LdcI41, - StFld(FieldData(6, "e", 57L)), - LdLoc0, - LdFtn(MethodDefData(3, 0, 131, "
b__0", 64L, Vector(ParamData(0, 1, "x")))), - NewObj(MemberRefData(TypeSpecData(28L), ".ctor", 35L)), - StLoc1, - LdLoc1, - LdcI43, - CallVirt(MemberRefData(TypeSpecData(28L), "Invoke", 41L)), - StLoc2, - Ret - ), - 2, - Some(16L) - ), - Method( - List(LdArg0, Call(MemberRefData(TypeRefData(6L, "Object", "System"), ".ctor", 6L)), Nop, Ret), - 0, - None - ), - Method( - List(LdArg0, Call(MemberRefData(TypeRefData(6L, "Object", "System"), ".ctor", 6L)), Nop, Ret), - 0, - None - ), - Method(List(LdArg1, LdArg0, LdFld(FieldData(6, "e", 57L)), Add, Ret), 0, None) - ) -signatures: |- - List( - (1L, MethodRefDefSig(true, false, false, false, 0, Tpe(Void, false), List(Tpe(I4, false)))), - (6L, MethodRefDefSig(true, false, false, false, 0, Tpe(Void, false), List())), - ( - 10L, - MethodRefDefSig( - true, - false, - false, - false, - 0, - Tpe(Void, false), - List(Tpe(ValueTpe(TypeRefData(15L, "DebuggingModes", "")), false)) - ) - ), - ( - 16L, - LocalVarSig( - List( - LocalVar( - Cls( - TypeDefData( - 2, - 1048835, - "<>c__DisplayClass0_0", - "", - TypeRefData(6L, "Object", "System"), - Vector(FieldData(6, "e", 57L)), - Vector( - MethodDefData(2, 0, 6278, ".ctor", 6L, Vector()), - MethodDefData(3, 0, 131, "
b__0", 64L, Vector(ParamData(0, 1, "x"))) - ) - ) - ), - false - ), - LocalVar(Generic(Cls(TypeRefData(6L, "Func`2", "System")), List(I4, I4)), false), - LocalVar(I4, false) - ) - ) - ), - (28L, TypeSig(Tpe(Generic(Cls(TypeRefData(6L, "Func`2", "System")), List(I4, I4)), false))), - ( - 35L, - MethodRefDefSig( - true, - false, - false, - false, - 0, - Tpe(Void, false), - List(Tpe(Object, false), Tpe(I, false)) - ) - ), - (41L, MethodRefDefSig(true, false, false, false, 0, Tpe(Var(1), false), List(Tpe(Var(0), false)))), - (57L, FieldSig(I4)), - (60L, MethodRefDefSig(false, false, false, false, 0, Tpe(Void, false), List())), - (64L, MethodRefDefSig(true, false, false, false, 0, Tpe(I4, false), List(Tpe(I4, false)))) - ) diff --git a/dotnet/src/test/resources/parser/compare.prs b/dotnet/src/test/resources/parser/compare.prs deleted file mode 100644 index 2e6944e6..00000000 --- a/dotnet/src/test/resources/parser/compare.prs +++ /dev/null @@ -1,553 +0,0 @@ -exe: compare.exe ---- -methods: |- - List( - Method( - List( - Nop, - LdcI41, - StLoc0, - LdcI42, - StLoc1, - LdcI43, - StLoc2, - LdcI44, - StLoc3, - LdcI45, - ConvI8, - StLocS(4), - LdcI46, - ConvI8, - StLocS(5), - LdcI40, - StLocS(6), - LdLoc0, - LdLoc1, - Ceq, - StLocS(6), - LdLoc0, - ConvI8, - LdLoc2, - ConvU8, - Ceq, - StLocS(6), - LdLoc2, - LdLoc3, - Ceq, - StLocS(6), - LdLoc2, - ConvU8, - LdLocS(4), - Ceq, - StLocS(6), - LdLocS(4), - LdLocS(5), - Ceq, - StLocS(6), - LdLoc0, - LdLoc1, - Ceq, - StLocS(7), - LdLocS(7), - BrFalseS(2), - Nop, - Nop, - LdLoc0, - ConvI8, - LdLoc2, - ConvU8, - Ceq, - StLocS(8), - LdLocS(8), - BrFalseS(2), - Nop, - Nop, - LdLoc2, - LdLoc3, - Ceq, - StLocS(9), - LdLocS(9), - BrFalseS(2), - Nop, - Nop, - LdLoc2, - ConvU8, - LdLocS(4), - Ceq, - StLocS(10), - LdLocS(10), - BrFalseS(2), - Nop, - Nop, - LdLocS(4), - LdLocS(5), - Ceq, - StLocS(11), - LdLocS(11), - BrFalseS(2), - Nop, - Nop, - LdLoc0, - LdLoc1, - Clt, - LdcI40, - Ceq, - StLocS(6), - LdLoc0, - ConvI8, - LdLoc2, - ConvU8, - Clt, - LdcI40, - Ceq, - StLocS(6), - LdLoc2, - LdLoc3, - CltUn, - LdcI40, - Ceq, - StLocS(6), - LdLoc2, - ConvU8, - LdLocS(4), - Clt, - LdcI40, - Ceq, - StLocS(6), - LdLocS(4), - LdLocS(5), - Clt, - LdcI40, - Ceq, - StLocS(6), - LdLoc0, - LdLoc1, - Clt, - LdcI40, - Ceq, - StLocS(12), - LdLocS(12), - BrFalseS(2), - Nop, - Nop, - LdLoc0, - ConvI8, - LdLoc2, - ConvU8, - Clt, - LdcI40, - Ceq, - StLocS(13), - LdLocS(13), - BrFalseS(2), - Nop, - Nop, - LdLoc2, - LdLoc3, - CltUn, - LdcI40, - Ceq, - StLocS(14), - LdLocS(14), - BrFalseS(2), - Nop, - Nop, - LdLoc2, - ConvU8, - LdLocS(4), - Clt, - LdcI40, - Ceq, - StLocS(15), - LdLocS(15), - BrFalseS(2), - Nop, - Nop, - LdLocS(4), - LdLocS(5), - Clt, - LdcI40, - Ceq, - StLocS(16), - LdLocS(16), - BrFalseS(2), - Nop, - Nop, - LdLoc0, - LdLoc1, - Cgt, - LdcI40, - Ceq, - StLocS(6), - LdLoc0, - ConvI8, - LdLoc2, - ConvU8, - Cgt, - LdcI40, - Ceq, - StLocS(6), - LdLoc2, - LdLoc3, - CgtUn, - LdcI40, - Ceq, - StLocS(6), - LdLoc2, - ConvU8, - LdLocS(4), - Cgt, - LdcI40, - Ceq, - StLocS(6), - LdLocS(4), - LdLocS(5), - Cgt, - LdcI40, - Ceq, - StLocS(6), - LdLoc0, - LdLoc1, - Cgt, - LdcI40, - Ceq, - StLocS(17), - LdLocS(17), - BrFalseS(2), - Nop, - Nop, - LdLoc0, - ConvI8, - LdLoc2, - ConvU8, - Cgt, - LdcI40, - Ceq, - StLocS(18), - LdLocS(18), - BrFalseS(2), - Nop, - Nop, - LdLoc2, - LdLoc3, - CgtUn, - LdcI40, - Ceq, - StLocS(19), - LdLocS(19), - BrFalseS(2), - Nop, - Nop, - LdLoc2, - ConvU8, - LdLocS(4), - Cgt, - LdcI40, - Ceq, - StLocS(20), - LdLocS(20), - BrFalseS(2), - Nop, - Nop, - LdLocS(4), - LdLocS(5), - Cgt, - LdcI40, - Ceq, - StLocS(21), - LdLocS(21), - BrFalseS(2), - Nop, - Nop, - LdLoc0, - LdLoc1, - Ceq, - LdcI40, - Ceq, - StLocS(6), - LdLoc0, - ConvI8, - LdLoc2, - ConvU8, - Ceq, - LdcI40, - Ceq, - StLocS(6), - LdLoc2, - LdLoc3, - Ceq, - LdcI40, - Ceq, - StLocS(6), - LdLoc2, - ConvU8, - LdLocS(4), - Ceq, - LdcI40, - Ceq, - StLocS(6), - LdLocS(4), - LdLocS(5), - Ceq, - LdcI40, - Ceq, - StLocS(6), - LdLoc0, - LdLoc1, - Ceq, - LdcI40, - Ceq, - StLocS(22), - LdLocS(22), - BrFalseS(2), - Nop, - Nop, - LdLoc0, - ConvI8, - LdLoc2, - ConvU8, - Ceq, - LdcI40, - Ceq, - StLocS(23), - LdLocS(23), - BrFalseS(2), - Nop, - Nop, - LdLoc2, - LdLoc3, - Ceq, - LdcI40, - Ceq, - StLocS(24), - LdLocS(24), - BrFalseS(2), - Nop, - Nop, - LdLoc2, - ConvU8, - LdLocS(4), - Ceq, - LdcI40, - Ceq, - StLocS(25), - LdLocS(25), - BrFalseS(2), - Nop, - Nop, - LdLocS(4), - LdLocS(5), - Ceq, - LdcI40, - Ceq, - StLocS(26), - LdLocS(26), - BrFalseS(2), - Nop, - Nop, - LdLoc0, - LdLoc1, - Cgt, - StLocS(6), - LdLoc0, - ConvI8, - LdLoc2, - ConvU8, - Cgt, - StLocS(6), - LdLoc2, - LdLoc3, - CgtUn, - StLocS(6), - LdLoc2, - ConvU8, - LdLocS(4), - Cgt, - StLocS(6), - LdLocS(4), - LdLocS(5), - Cgt, - StLocS(6), - LdLoc0, - LdLoc1, - Cgt, - StLocS(27), - LdLocS(27), - BrFalseS(2), - Nop, - Nop, - LdLoc0, - ConvI8, - LdLoc2, - ConvU8, - Cgt, - StLocS(28), - LdLocS(28), - BrFalseS(2), - Nop, - Nop, - LdLoc2, - LdLoc3, - CgtUn, - StLocS(29), - LdLocS(29), - BrFalseS(2), - Nop, - Nop, - LdLoc2, - ConvU8, - LdLocS(4), - Cgt, - StLocS(30), - LdLocS(30), - BrFalseS(2), - Nop, - Nop, - LdLocS(4), - LdLocS(5), - Cgt, - StLocS(31), - LdLocS(31), - BrFalseS(2), - Nop, - Nop, - LdLoc0, - LdLoc1, - Clt, - StLocS(6), - LdLoc0, - ConvI8, - LdLoc2, - ConvU8, - Clt, - StLocS(6), - LdLoc2, - LdLoc3, - CltUn, - StLocS(6), - LdLoc2, - ConvU8, - LdLocS(4), - Clt, - StLocS(6), - LdLocS(4), - LdLocS(5), - Clt, - StLocS(6), - LdLoc0, - LdLoc1, - Clt, - StLocS(32), - LdLocS(32), - BrFalseS(2), - Nop, - Nop, - LdLoc0, - ConvI8, - LdLoc2, - ConvU8, - Clt, - StLocS(33), - LdLocS(33), - BrFalseS(2), - Nop, - Nop, - LdLoc2, - LdLoc3, - CltUn, - StLocS(34), - LdLocS(34), - BrFalseS(2), - Nop, - Nop, - LdLoc2, - ConvU8, - LdLocS(4), - Clt, - StLocS(35), - LdLocS(35), - BrFalseS(2), - Nop, - Nop, - LdLocS(4), - LdLocS(5), - Clt, - StLocS(36), - LdLocS(36), - BrFalseS(2), - Nop, - Nop, - Ret - ), - 2, - Some(16L) - ), - Method(List(Nop, Ret), 0, None), - Method( - List(LdArg0, Call(MemberRefData(TypeRefData(6L, "Object", "System"), ".ctor", 6L)), Nop, Ret), - 0, - None - ) - ) -signatures: |- - List( - (1L, MethodRefDefSig(true, false, false, false, 0, Tpe(Void, false), List(Tpe(I4, false)))), - (6L, MethodRefDefSig(true, false, false, false, 0, Tpe(Void, false), List())), - ( - 10L, - MethodRefDefSig( - true, - false, - false, - false, - 0, - Tpe(Void, false), - List(Tpe(ValueTpe(TypeRefData(15L, "DebuggingModes", "")), false)) - ) - ), - ( - 16L, - LocalVarSig( - List( - LocalVar(I4, false), - LocalVar(I4, false), - LocalVar(U4, false), - LocalVar(U4, false), - LocalVar(I8, false), - LocalVar(I8, false), - LocalVar(Boolean, false), - LocalVar(Boolean, false), - LocalVar(Boolean, false), - LocalVar(Boolean, false), - LocalVar(Boolean, false), - LocalVar(Boolean, false), - LocalVar(Boolean, false), - LocalVar(Boolean, false), - LocalVar(Boolean, false), - LocalVar(Boolean, false), - LocalVar(Boolean, false), - LocalVar(Boolean, false), - LocalVar(Boolean, false), - LocalVar(Boolean, false), - LocalVar(Boolean, false), - LocalVar(Boolean, false), - LocalVar(Boolean, false), - LocalVar(Boolean, false), - LocalVar(Boolean, false), - LocalVar(Boolean, false), - LocalVar(Boolean, false), - LocalVar(Boolean, false), - LocalVar(Boolean, false), - LocalVar(Boolean, false), - LocalVar(Boolean, false), - LocalVar(Boolean, false), - LocalVar(Boolean, false), - LocalVar(Boolean, false), - LocalVar(Boolean, false), - LocalVar(Boolean, false), - LocalVar(Boolean, false) - ) - ) - ), - (65L, MethodRefDefSig(false, false, false, false, 0, Tpe(Void, false), List())) - ) diff --git a/dotnet/src/test/resources/parser/if.prs b/dotnet/src/test/resources/parser/if.prs deleted file mode 100644 index 1ae1b34e..00000000 --- a/dotnet/src/test/resources/parser/if.prs +++ /dev/null @@ -1,160 +0,0 @@ -exe: if.exe ---- -methods: |- - List( - Method( - List( - Nop, - LdcI4S(10), - StLoc0, - LdLoc0, - LdcI41, - Clt, - StLoc1, - LdLoc1, - BrFalseS(4), - Nop, - LdcI44, - StLoc0, - Nop, - LdLoc0, - LdcI45, - Cgt, - StLoc2, - LdLoc2, - BrFalseS(14), - Nop, - LdLoc0, - LdcI46, - Cgt, - StLoc3, - LdLoc3, - BrFalseS(4), - Nop, - LdcI47, - StLoc0, - Nop, - Nop, - LdLoc0, - LdcI40, - Cgt, - StLocS(4), - LdLocS(4), - BrFalseS(6), - Nop, - LdcI44, - StLoc0, - Nop, - BrS(4), - Nop, - LdcI45, - StLoc0, - Nop, - LdLoc0, - LdcI42, - BleS(6), - LdLoc0, - LdcI44, - Clt, - BrS(1), - LdcI40, - StLocS(5), - LdLocS(5), - BrFalseS(6), - Nop, - LdcI46, - StLoc0, - Nop, - BrS(4), - Nop, - LdcI48, - StLoc0, - Nop, - LdLoc0, - LdcI47, - BgtS(7), - LdLoc0, - LdcI4S(10), - Cgt, - BrS(1), - LdcI41, - StLocS(6), - LdLocS(6), - BrFalseS(6), - Nop, - LdcI41, - StLoc0, - Nop, - BrS(4), - Nop, - LdcI40, - StLoc0, - Nop, - LdLoc0, - LdcI41, - BleS(4), - LdLoc0, - LdcI43, - BltS(7), - LdLoc0, - LdcI4S(20), - Cgt, - BrS(1), - LdcI41, - StLocS(7), - LdLocS(7), - BrFalseS(6), - Nop, - LdcI42, - StLoc0, - Nop, - BrS(4), - Nop, - LdcI43, - StLoc0, - Nop, - Ret - ), - 2, - Some(16L) - ), - Method(List(Nop, Ret), 0, None), - Method( - List(LdArg0, Call(MemberRefData(TypeRefData(6L, "Object", "System"), ".ctor", 6L)), Nop, Ret), - 0, - None - ) - ) -signatures: |- - List( - (1L, MethodRefDefSig(true, false, false, false, 0, Tpe(Void, false), List(Tpe(I4, false)))), - (6L, MethodRefDefSig(true, false, false, false, 0, Tpe(Void, false), List())), - ( - 10L, - MethodRefDefSig( - true, - false, - false, - false, - 0, - Tpe(Void, false), - List(Tpe(ValueTpe(TypeRefData(15L, "DebuggingModes", "")), false)) - ) - ), - ( - 16L, - LocalVarSig( - List( - LocalVar(I4, false), - LocalVar(Boolean, false), - LocalVar(Boolean, false), - LocalVar(Boolean, false), - LocalVar(Boolean, false), - LocalVar(Boolean, false), - LocalVar(Boolean, false), - LocalVar(Boolean, false) - ) - ) - ), - (36L, MethodRefDefSig(false, false, false, false, 0, Tpe(Void, false), List())) - ) diff --git a/dotnet/src/test/resources/parser/inheritance.prs b/dotnet/src/test/resources/parser/inheritance.prs deleted file mode 100644 index 02290966..00000000 --- a/dotnet/src/test/resources/parser/inheritance.prs +++ /dev/null @@ -1,198 +0,0 @@ -exe: inheritance.exe ---- -methods: |- - List( - Method( - List( - LdArg0, - Call(MemberRefData(TypeRefData(6L, "Object", "System"), ".ctor", 6L)), - Nop, - Nop, - Ret - ), - 0, - None - ), - Method( - List( - Nop, - LdArg0, - CallVirt(MethodDefData(2, 0, 454, "Answer", 43L, Vector())), - LdcI41, - Add, - StLoc0, - BrS(0), - LdLoc0, - Ret - ), - 2, - Some(16L) - ), - Method(List(Nop, LdcI40, StLoc0, BrS(0), LdLoc0, Ret), 1, Some(16L)), - Method( - List( - LdArg0, - LdArg1, - Call(MethodDefData(0, 0, 6278, ".ctor", 1L, Vector(ParamData(0, 1, "val")))), - Nop, - Nop, - LdArg0, - LdArg1, - StFld(FieldData(1, "AVal", 40L)), - Ret - ), - 0, - None - ), - Method( - List( - Nop, - LdArg0, - LdFld(FieldData(1, "AVal", 40L)), - LdcI4S(42), - Add, - StLoc0, - BrS(0), - LdLoc0, - Ret - ), - 2, - Some(16L) - ), - Method( - List( - LdArg0, - LdArg1, - Call(MethodDefData(0, 0, 6278, ".ctor", 1L, Vector(ParamData(0, 1, "val")))), - Nop, - Nop, - LdArg0, - LdArg1, - StFld(FieldData(1, "BVal", 40L)), - Ret - ), - 0, - None - ), - Method( - List( - Nop, - LdArg0, - LdFld(FieldData(1, "BVal", 40L)), - LdcI4S(43), - Add, - StLoc0, - BrS(0), - LdLoc0, - Ret - ), - 2, - Some(16L) - ), - Method( - List( - Nop, - LdcI4S(100), - NewObj(MethodDefData(3, 0, 6278, ".ctor", 1L, Vector(ParamData(0, 1, "aVal")))), - StLoc0, - LdcI4(200), - NewObj(MethodDefData(5, 0, 6278, ".ctor", 1L, Vector(ParamData(0, 1, "bVal")))), - StLoc1, - LdLoc0, - CallVirt(MethodDefData(2, 0, 454, "Answer", 43L, Vector())), - LdLoc1, - CallVirt(MethodDefData(2, 0, 454, "Answer", 43L, Vector())), - Add, - StLoc2, - LdLoc0, - CallVirt(MethodDefData(1, 0, 454, "AnswerPlus1", 43L, Vector())), - StLoc3, - LdLoc1, - CallVirt(MethodDefData(1, 0, 454, "AnswerPlus1", 43L, Vector())), - StLocS(4), - LdLoc3, - LdLocS(4), - Add, - StLocS(5), - BrS(0), - LdLocS(5), - Ret - ), - 2, - Some(20L) - ), - Method(List(Nop, Ret), 0, None), - Method( - List(LdArg0, Call(MemberRefData(TypeRefData(6L, "Object", "System"), ".ctor", 6L)), Nop, Ret), - 0, - None - ) - ) -signatures: |- - List( - (1L, MethodRefDefSig(true, false, false, false, 0, Tpe(Void, false), List(Tpe(I4, false)))), - (6L, MethodRefDefSig(true, false, false, false, 0, Tpe(Void, false), List())), - ( - 10L, - MethodRefDefSig( - true, - false, - false, - false, - 0, - Tpe(Void, false), - List(Tpe(ValueTpe(TypeRefData(15L, "DebuggingModes", "")), false)) - ) - ), - (16L, LocalVarSig(List(LocalVar(I4, false)))), - ( - 20L, - LocalVarSig( - List( - LocalVar( - Cls( - TypeDefData( - 1, - 1048577, - "Parent", - "", - TypeRefData(6L, "Object", "System"), - Vector(), - Vector( - MethodDefData(0, 0, 6278, ".ctor", 1L, Vector(ParamData(0, 1, "val"))), - MethodDefData(1, 0, 454, "AnswerPlus1", 43L, Vector()), - MethodDefData(2, 0, 454, "Answer", 43L, Vector()) - ) - ) - ), - false - ), - LocalVar( - Cls( - TypeDefData( - 1, - 1048577, - "Parent", - "", - TypeRefData(6L, "Object", "System"), - Vector(), - Vector( - MethodDefData(0, 0, 6278, ".ctor", 1L, Vector(ParamData(0, 1, "val"))), - MethodDefData(1, 0, 454, "AnswerPlus1", 43L, Vector()), - MethodDefData(2, 0, 454, "Answer", 43L, Vector()) - ) - ) - ), - false - ), - LocalVar(I4, false), - LocalVar(I4, false), - LocalVar(I4, false), - LocalVar(I4, false) - ) - ) - ), - (40L, FieldSig(I4)), - (43L, MethodRefDefSig(true, false, false, false, 0, Tpe(I4, false), List())), - (47L, MethodRefDefSig(false, false, false, false, 0, Tpe(Void, false), List())) - ) diff --git a/dotnet/src/test/resources/parser/method_calling.prs b/dotnet/src/test/resources/parser/method_calling.prs deleted file mode 100644 index 5c1d131e..00000000 --- a/dotnet/src/test/resources/parser/method_calling.prs +++ /dev/null @@ -1,113 +0,0 @@ -exe: method_calling.exe ---- -methods: |- - List( - Method(List(Nop, LdcI4S(42), StLoc0, BrS(0), LdLoc0, Ret), 1, Some(16L)), - Method(List(Nop, LdcI4S(42), StLoc0, BrS(0), LdLoc0, Ret), 1, Some(16L)), - Method(List(Nop, LdArg0, LdArg1, Add, StLoc0, BrS(0), LdLoc0, Ret), 2, Some(16L)), - Method(List(Nop, LdcI4S(42), StLoc0, BrS(0), LdLoc0, Ret), 1, Some(16L)), - Method(List(Nop, LdcI4S(42), StLoc0, BrS(0), LdLoc0, Ret), 1, Some(16L)), - Method( - List( - Nop, - Call(MethodDefData(0, 0, 150, "answer", 39L, Vector())), - StLoc0, - Call(MethodDefData(1, 0, 145, "secretAnswer", 39L, Vector())), - StLoc1, - LdLoc0, - LdLoc1, - Call(MethodDefData(2, 0, 150, "sum", 43L, Vector(ParamData(0, 1, "a"), ParamData(0, 2, "b")))), - StLoc2, - NewObj(MethodDefData(6, 0, 6278, ".ctor", 6L, Vector())), - StLoc3, - LdLoc3, - CallVirt(MethodDefData(3, 0, 134, "personalAnswer", 49L, Vector())), - StLocS(4), - LdLoc3, - CallVirt(MethodDefData(4, 0, 129, "personalSecretAnswer", 49L, Vector())), - StLocS(5), - Ret - ), - 2, - Some(20L) - ), - Method( - List(LdArg0, Call(MemberRefData(TypeRefData(6L, "Object", "System"), ".ctor", 6L)), Nop, Ret), - 0, - None - ) - ) -signatures: |- - List( - (1L, MethodRefDefSig(true, false, false, false, 0, Tpe(Void, false), List(Tpe(I4, false)))), - (6L, MethodRefDefSig(true, false, false, false, 0, Tpe(Void, false), List())), - ( - 10L, - MethodRefDefSig( - true, - false, - false, - false, - 0, - Tpe(Void, false), - List(Tpe(ValueTpe(TypeRefData(15L, "DebuggingModes", "")), false)) - ) - ), - (16L, LocalVarSig(List(LocalVar(I4, false)))), - ( - 20L, - LocalVarSig( - List( - LocalVar(I4, false), - LocalVar(I4, false), - LocalVar(I4, false), - LocalVar( - Cls( - TypeDefData( - 1, - 1048577, - "Program", - "", - TypeRefData(6L, "Object", "System"), - Vector(), - Vector( - MethodDefData(0, 0, 150, "answer", 39L, Vector()), - MethodDefData(1, 0, 145, "secretAnswer", 39L, Vector()), - MethodDefData( - 2, - 0, - 150, - "sum", - 43L, - Vector(ParamData(0, 1, "a"), ParamData(0, 2, "b")) - ), - MethodDefData(3, 0, 134, "personalAnswer", 49L, Vector()), - MethodDefData(4, 0, 129, "personalSecretAnswer", 49L, Vector()), - MethodDefData(5, 0, 150, "Main", 53L, Vector()), - MethodDefData(6, 0, 6278, ".ctor", 6L, Vector()) - ) - ) - ), - false - ), - LocalVar(I4, false), - LocalVar(I4, false) - ) - ) - ), - (39L, MethodRefDefSig(false, false, false, false, 0, Tpe(I4, false), List())), - ( - 43L, - MethodRefDefSig( - false, - false, - false, - false, - 0, - Tpe(I4, false), - List(Tpe(I4, false), Tpe(I4, false)) - ) - ), - (49L, MethodRefDefSig(true, false, false, false, 0, Tpe(I4, false), List())), - (53L, MethodRefDefSig(false, false, false, false, 0, Tpe(Void, false), List())) - ) diff --git a/dotnet/src/test/resources/parser/objects.prs b/dotnet/src/test/resources/parser/objects.prs deleted file mode 100644 index 41d5b66f..00000000 --- a/dotnet/src/test/resources/parser/objects.prs +++ /dev/null @@ -1,157 +0,0 @@ -exe: objects.exe ---- -methods: |- - List( - Method( - List( - LdArg0, - Call(MemberRefData(TypeRefData(6L, "Object", "System"), ".ctor", 6L)), - Nop, - Nop, - LdArg0, - LdArg1, - StFld(FieldData(1, "AVal", 38L)), - Ret - ), - 0, - None - ), - Method( - List( - Nop, - LdArg0, - LdFld(FieldData(1, "AVal", 38L)), - LdcI4S(42), - Add, - StLoc0, - BrS(0), - LdLoc0, - Ret - ), - 2, - Some(16L) - ), - Method( - List( - LdArg0, - Call(MemberRefData(TypeRefData(6L, "Object", "System"), ".ctor", 6L)), - Nop, - Nop, - LdArg0, - LdArg1, - StFld(FieldData(1, "BVal", 38L)), - Ret - ), - 0, - None - ), - Method( - List( - Nop, - LdArg0, - LdFld(FieldData(1, "BVal", 38L)), - LdcI4S(43), - Add, - StLoc0, - BrS(0), - LdLoc0, - Ret - ), - 2, - Some(16L) - ), - Method( - List( - Nop, - LdcI4S(100), - NewObj(MethodDefData(0, 0, 6278, ".ctor", 1L, Vector(ParamData(0, 1, "aVal")))), - StLoc0, - LdcI4(200), - NewObj(MethodDefData(2, 0, 6278, ".ctor", 1L, Vector(ParamData(0, 1, "bVal")))), - StLoc1, - LdLoc0, - CallVirt(MethodDefData(1, 0, 134, "AnswerA", 41L, Vector())), - LdLoc1, - CallVirt(MethodDefData(3, 0, 134, "AnswerB", 41L, Vector())), - Add, - StLoc2, - LdLoc2, - StLoc3, - BrS(0), - LdLoc3, - Ret - ), - 2, - Some(20L) - ), - Method(List(Nop, Ret), 0, None), - Method( - List(LdArg0, Call(MemberRefData(TypeRefData(6L, "Object", "System"), ".ctor", 6L)), Nop, Ret), - 0, - None - ) - ) -signatures: |- - List( - (1L, MethodRefDefSig(true, false, false, false, 0, Tpe(Void, false), List(Tpe(I4, false)))), - (6L, MethodRefDefSig(true, false, false, false, 0, Tpe(Void, false), List())), - ( - 10L, - MethodRefDefSig( - true, - false, - false, - false, - 0, - Tpe(Void, false), - List(Tpe(ValueTpe(TypeRefData(15L, "DebuggingModes", "")), false)) - ) - ), - (16L, LocalVarSig(List(LocalVar(I4, false)))), - ( - 20L, - LocalVarSig( - List( - LocalVar( - Cls( - TypeDefData( - 1, - 1048577, - "A", - "", - TypeRefData(6L, "Object", "System"), - Vector(FieldData(1, "AVal", 38L)), - Vector( - MethodDefData(0, 0, 6278, ".ctor", 1L, Vector(ParamData(0, 1, "aVal"))), - MethodDefData(1, 0, 134, "AnswerA", 41L, Vector()) - ) - ) - ), - false - ), - LocalVar( - Cls( - TypeDefData( - 2, - 1048577, - "B", - "", - TypeRefData(6L, "Object", "System"), - Vector(FieldData(1, "BVal", 38L)), - Vector( - MethodDefData(2, 0, 6278, ".ctor", 1L, Vector(ParamData(0, 1, "bVal"))), - MethodDefData(3, 0, 134, "AnswerB", 41L, Vector()) - ) - ) - ), - false - ), - LocalVar(I4, false), - LocalVar(I4, false) - ) - ) - ), - (38L, FieldSig(I4)), - (41L, MethodRefDefSig(true, false, false, false, 0, Tpe(I4, false), List())), - (45L, MethodRefDefSig(false, false, false, false, 0, Tpe(Void, false), List())) - ) diff --git a/dotnet/src/test/resources/translation/Arithmetics.trs b/dotnet/src/test/resources/translation/Arithmetics.trs new file mode 100644 index 00000000..2d489c27 --- /dev/null +++ b/dotnet/src/test/resources/translation/Arithmetics.trs @@ -0,0 +1,133 @@ +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: Arithmetics.exe + sources: + - Pravda.dll + - dotnet-tests/resources/Arithmetics.cs + optimize: true +--- +translation: |- + meta custom "CIL" + meta program_name "Arithmetics" + dup + push "ctor" + eq + jumpi @method_ctor + push "init" + sexist + jumpi @methods + push "Program was not initialized" + throw + @methods: + dup + push "TestBasicOperations" + eq + jumpi @method_TestBasicOperations + push "Wrong method name" + throw + @method_TestBasicOperations: + meta method { + "name":"TestBasicOperations","returnTpe":int8(3) + } + push null + push null + push null + meta source_mark { + "sl":int32(10),"sc":int32(9),"el":int32(10),"src":"$PRAVDA_TMP_DIR/Arithmetics.cs","ec":int32(23) + } + push "p_X" + sget + push int32(2) + add + meta source_mark { + "sl":int32(11),"sc":int32(9),"el":int32(11),"src":"$PRAVDA_TMP_DIR/Arithmetics.cs","ec":int32(23) + } + push "p_X" + sget + push int32(2) + mul + push int32(5) + swapn + pop + meta source_mark { + "sl":int32(12),"sc":int32(9),"el":int32(12),"src":"$PRAVDA_TMP_DIR/Arithmetics.cs","ec":int32(23) + } + push "p_X" + sget + push int32(2) + swap + div + push int32(4) + swapn + pop + meta source_mark { + "sl":int32(13),"sc":int32(9),"el":int32(13),"src":"$PRAVDA_TMP_DIR/Arithmetics.cs","ec":int32(23) + } + push "p_X" + sget + push int32(2) + swap + mod + push int32(3) + swapn + pop + meta source_mark { + "sl":int32(14),"sc":int32(9),"el":int32(14),"src":"$PRAVDA_TMP_DIR/Arithmetics.cs","ec":int32(46) + } + push int32(4) + dupn + add + push int32(42) + add + push int32(3) + dupn + mul + push int32(2) + dupn + add + push int32(1337) + swap + div + jump @TestBasicOperations_lvc + @TestBasicOperations_lvc: + swap + pop + swap + pop + swap + pop + swap + pop + jump @stop + @method_ctor: + meta method { + "name":"ctor","returnTpe":int8(0) + } + push "init" + sexist + not + jumpi @ctor_ok + push "Program has been already initialized" + throw + @ctor_ok: + push null + push "init" + sput + push int32(0) + push "p_X" + sput + meta source_mark { + "sl":int32(6),"sc":int32(5),"el":int32(6),"src":"$PRAVDA_TMP_DIR/Arithmetics.cs","ec":int32(24) + } + push int32(10) + push "p_X" + sput + jump @ctor_lvc + @ctor_lvc: + pop + jump @stop + @stop: diff --git a/dotnet/src/test/resources/translation/Arrays.trs b/dotnet/src/test/resources/translation/Arrays.trs new file mode 100644 index 00000000..6ac0e8b6 --- /dev/null +++ b/dotnet/src/test/resources/translation/Arrays.trs @@ -0,0 +1,384 @@ +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: Arrays.exe + sources: + - Pravda.dll + - dotnet-tests/resources/Arrays.cs + optimize: true +--- +translation: |- + meta custom "CIL" + meta program_name "Arrays" + dup + push "ctor" + eq + jumpi @method_ctor + push "init" + sexist + jumpi @methods + push "Program was not initialized" + throw + @methods: + dup + push "TestAllArrays" + eq + jumpi @method_TestAllArrays + dup + push "TestByteArrays" + eq + jumpi @method_TestByteArrays + push "Wrong method name" + throw + @method_TestAllArrays: + meta method { + "name":"TestAllArrays","returnTpe":int8(0) + } + push null + push null + push null + push null + push null + meta source_mark { + "sl":int32(33),"sc":int32(9),"el":int32(33),"src":"$PRAVDA_TMP_DIR/Arrays.cs","ec":int32(53) + } + new int16[97, 98, 99] + push int32(6) + swapn + pop + meta source_mark { + "sl":int32(34),"sc":int32(9),"el":int32(34),"src":"$PRAVDA_TMP_DIR/Arrays.cs","ec":int32(44) + } + new int32[1, 2, 3] + push int32(5) + swapn + pop + meta source_mark { + "sl":int32(35),"sc":int32(9),"el":int32(35),"src":"$PRAVDA_TMP_DIR/Arrays.cs","ec":int32(59) + } + new number[1.0, 2.0, 3.0] + push int32(4) + swapn + pop + meta source_mark { + "sl":int32(36),"sc":int32(9),"el":int32(36),"src":"$PRAVDA_TMP_DIR/Arrays.cs","ec":int32(65) + } + push int32(3) + push int8(11) + new_array + dup + push int32(0) + push "abc" + swap + array_mut + dup + push int32(1) + push "def" + swap + array_mut + dup + push int32(2) + push "rty" + swap + array_mut + push int32(3) + swapn + pop + meta source_mark { + "sl":int32(37),"sc":int32(9),"el":int32(37),"src":"$PRAVDA_TMP_DIR/Arrays.cs","ec":int32(50) + } + new int64[4, 5, 6] + push int32(2) + swapn + pop + meta source_mark { + "sl":int32(39),"sc":int32(9),"el":int32(39),"src":"$PRAVDA_TMP_DIR/Arrays.cs","ec":int32(24) + } + push int32(5) + dupn + push int32(1) + push int32(100) + swap + array_mut + meta source_mark { + "sl":int32(40),"sc":int32(9),"el":int32(40),"src":"$PRAVDA_TMP_DIR/Arrays.cs","ec":int32(21) + } + push int32(4) + dupn + push int32(1) + push int32(4) + swap + array_mut + meta source_mark { + "sl":int32(41),"sc":int32(9),"el":int32(41),"src":"$PRAVDA_TMP_DIR/Arrays.cs","ec":int32(26) + } + push int32(3) + dupn + push int32(1) + push number(4.0) + swap + array_mut + meta source_mark { + "sl":int32(42),"sc":int32(9),"el":int32(42),"src":"$PRAVDA_TMP_DIR/Arrays.cs","ec":int32(29) + } + push int32(2) + dupn + push int32(1) + push "asdf" + swap + array_mut + meta source_mark { + "sl":int32(43),"sc":int32(9),"el":int32(43),"src":"$PRAVDA_TMP_DIR/Arrays.cs","ec":int32(23) + } + push int32(1) + dupn + push int32(1) + push int32(7) + push int8(5) + cast + swap + array_mut + meta source_mark { + "sl":int32(45),"sc":int32(9),"el":int32(45),"src":"$PRAVDA_TMP_DIR/Arrays.cs","ec":int32(34) + } + push int32(2) + dupn + length + push int8(3) + cast + pop + meta source_mark { + "sl":int32(46),"sc":int32(5),"el":int32(46),"src":"$PRAVDA_TMP_DIR/Arrays.cs","ec":int32(6) + } + jump @TestAllArrays_lvc + @TestAllArrays_lvc: + pop + pop + pop + pop + pop + pop + jump @stop + @method_TestByteArrays: + meta method { + "name":"TestByteArrays","returnTpe":int8(0) + } + push null + push null + push null + meta source_mark { + "sl":int32(10),"sc":int32(9),"el":int32(10),"src":"$PRAVDA_TMP_DIR/Arrays.cs","ec":int32(46) + } + new int8[1, 2, 3] + push int32(4) + swapn + pop + meta source_mark { + "sl":int32(11),"sc":int32(9),"el":int32(11),"src":"$PRAVDA_TMP_DIR/Arrays.cs","ec":int32(43) + } + new int8[4, 5, 6] + call @stdlib_array_to_bytes + push int32(3) + swapn + pop + meta source_mark { + "sl":int32(12),"sc":int32(9),"el":int32(12),"src":"$PRAVDA_TMP_DIR/Arrays.cs","ec":int32(43) + } + new int8[7, 8, 9] + call @stdlib_array_to_bytes + push int32(2) + swapn + pop + meta source_mark { + "sl":int32(14),"sc":int32(9),"el":int32(14),"src":"$PRAVDA_TMP_DIR/Arrays.cs","ec":int32(28) + } + push int32(3) + dupn + push int32(0) + array_get + pop + meta source_mark { + "sl":int32(15),"sc":int32(9),"el":int32(15),"src":"$PRAVDA_TMP_DIR/Arrays.cs","ec":int32(28) + } + push int32(3) + dupn + push int32(2) + array_get + pop + meta source_mark { + "sl":int32(16),"sc":int32(9),"el":int32(16),"src":"$PRAVDA_TMP_DIR/Arrays.cs","ec":int32(30) + } + push int32(2) + dupn + push int32(1) + array_get + pop + meta source_mark { + "sl":int32(17),"sc":int32(9),"el":int32(17),"src":"$PRAVDA_TMP_DIR/Arrays.cs","ec":int32(30) + } + push int32(1) + dupn + push int32(1) + array_get + pop + meta source_mark { + "sl":int32(19),"sc":int32(9),"el":int32(19),"src":"$PRAVDA_TMP_DIR/Arrays.cs","ec":int32(43) + } + push int32(2) + dupn + push int32(1) + push int32(2) + push int32(2) + dupn + add + swap + slice + pop + meta source_mark { + "sl":int32(21),"sc":int32(9),"el":int32(21),"src":"$PRAVDA_TMP_DIR/Arrays.cs","ec":int32(39) + } + push x42797465734D617070696E67 + push int32(3) + dupn + push int32(3) + dupn + push int32(2) + dupn + push int8(14) + cast + push int32(4) + dupn + concat + sput + pop + pop + meta source_mark { + "sl":int32(22),"sc":int32(9),"el":int32(22),"src":"$PRAVDA_TMP_DIR/Arrays.cs","ec":int32(59) + } + push x42797465734D617070696E67 + new int8[8, 9, 10] + call @stdlib_array_to_bytes + push int8(14) + cast + swap + concat + sexist + push int8(3) + cast + push int8(9) + cast + not + push int8(3) + cast + push int32(0) + eq + not + jumpi @TestByteArrays_br179 + meta source_mark { + "sl":int32(23),"sc":int32(11),"el":int32(23),"src":"$PRAVDA_TMP_DIR/Arrays.cs","ec":int32(53) + } + push x42797465734D617070696E67 + push int32(3) + dupn + new int8[7, 8, 9] + call @stdlib_array_to_bytes + push int32(2) + dupn + push int8(14) + cast + push int32(4) + dupn + concat + sput + pop + pop + @TestByteArrays_br179: + push int32(3) + dupn + meta source_mark { + "sl":int32(26),"sc":int32(9),"el":int32(26),"src":"$PRAVDA_TMP_DIR/Arrays.cs","ec":int32(21) + } + push int32(0) + push int32(2) + swap + array_mut + push int32(3) + dupn + meta source_mark { + "sl":int32(27),"sc":int32(9),"el":int32(27),"src":"$PRAVDA_TMP_DIR/Arrays.cs","ec":int32(21) + } + push int32(1) + push int32(1) + swap + array_mut + push int32(2) + dupn + meta source_mark { + "sl":int32(29),"sc":int32(9),"el":int32(29),"src":"$PRAVDA_TMP_DIR/Arrays.cs","ec":int32(35) + } + length + pop + jump @TestByteArrays_lvc + @TestByteArrays_lvc: + pop + pop + pop + pop + jump @stop + @method_ctor: + meta method { + "name":"ctor","returnTpe":int8(0) + } + push "init" + sexist + not + jumpi @ctor_ok + push "Program has been already initialized" + throw + @ctor_ok: + push null + push "init" + sput + meta source_mark { + "sl":int32(7),"sc":int32(5),"el":int32(7),"src":"$PRAVDA_TMP_DIR/Arrays.cs","ec":int32(78) + } + jump @ctor_lvc + @ctor_lvc: + pop + jump @stop + @stdlib_array_to_bytes: + dup + length + push x + push int32(0) + @array_to_bytes_loop: + push int32(4) + dupn + push int32(2) + dupn + array_get + push int8(14) + cast + push int32(3) + dupn + concat + push int32(3) + swapn + pop + push int32(1) + add + dup + push int32(4) + dupn + gt + jumpi @array_to_bytes_loop + pop + swap + pop + swap + pop + ret + @stop: diff --git a/dotnet/src/test/resources/translation/Block.trs b/dotnet/src/test/resources/translation/Block.trs new file mode 100644 index 00000000..5297410d --- /dev/null +++ b/dotnet/src/test/resources/translation/Block.trs @@ -0,0 +1,80 @@ +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: Block.exe + sources: + - Pravda.dll + - dotnet-tests/resources/Block.cs + optimize: true +--- +translation: |- + meta custom "CIL" + meta program_name "Block" + dup + push "ctor" + eq + jumpi @method_ctor + push "init" + sexist + jumpi @methods + push "Program was not initialized" + throw + @methods: + dup + push "TestHeightMethod" + eq + jumpi @method_TestHeightMethod + dup + push "TestLastBlockHash" + eq + jumpi @method_TestLastBlockHash + push "Wrong method name" + throw + @method_TestHeightMethod: + meta method { + "name":"TestHeightMethod","returnTpe":int8(5) + } + meta source_mark { + "sl":int32(9),"sc":int32(9),"el":int32(9),"src":"$PRAVDA_TMP_DIR/Block.cs","ec":int32(30) + } + height + jump @TestHeightMethod_lvc + @TestHeightMethod_lvc: + swap + pop + jump @stop + @method_TestLastBlockHash: + meta method { + "name":"TestLastBlockHash","returnTpe":int8(14) + } + meta source_mark { + "sl":int32(14),"sc":int32(9),"el":int32(14),"src":"$PRAVDA_TMP_DIR/Block.cs","ec":int32(37) + } + hash + jump @TestLastBlockHash_lvc + @TestLastBlockHash_lvc: + swap + pop + jump @stop + @method_ctor: + meta method { + "name":"ctor","returnTpe":int8(0) + } + push "init" + sexist + not + jumpi @ctor_ok + push "Program has been already initialized" + throw + @ctor_ok: + push null + push "init" + sput + jump @ctor_lvc + @ctor_lvc: + pop + jump @stop + @stop: diff --git a/dotnet/src/test/resources/translation/Callers.trs b/dotnet/src/test/resources/translation/Callers.trs new file mode 100644 index 00000000..cb66e3e6 --- /dev/null +++ b/dotnet/src/test/resources/translation/Callers.trs @@ -0,0 +1,65 @@ +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: Callers.exe + sources: + - Pravda.dll + - dotnet-tests/resources/Callers.cs + optimize: true +--- +translation: |- + meta custom "CIL" + meta program_name "PcallNamespaceCallers" + dup + push "ctor" + eq + jumpi @method_ctor + push "init" + sexist + jumpi @methods + push "Program was not initialized" + throw + @methods: + dup + push "TestCallers" + eq + jumpi @method_TestCallers + push "Wrong method name" + throw + @method_TestCallers: + meta method { + "name":"TestCallers","returnTpe":int8(14) + } + meta source_mark { + "sl":int32(11),"sc":int32(12),"el":int32(11),"src":"$PRAVDA_TMP_DIR/Callers.cs","ec":int32(37) + } + callers + push int32(0) + array_get + jump @TestCallers_lvc + @TestCallers_lvc: + swap + pop + jump @stop + @method_ctor: + meta method { + "name":"ctor","returnTpe":int8(0) + } + push "init" + sexist + not + jumpi @ctor_ok + push "Program has been already initialized" + throw + @ctor_ok: + push null + push "init" + sput + jump @ctor_lvc + @ctor_lvc: + pop + jump @stop + @stop: diff --git a/dotnet/src/test/resources/translation/Compare.trs b/dotnet/src/test/resources/translation/Compare.trs new file mode 100644 index 00000000..7fce84a0 --- /dev/null +++ b/dotnet/src/test/resources/translation/Compare.trs @@ -0,0 +1,1477 @@ +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: Compare.exe + sources: + - Pravda.dll + - dotnet-tests/resources/Compare.cs + optimize: true +--- +translation: |- + meta custom "CIL" + meta program_name "Compare" + dup + push "ctor" + eq + jumpi @method_ctor + push "init" + sexist + jumpi @methods + push "Program was not initialized" + throw + @methods: + dup + push "TestCompare" + eq + jumpi @method_TestCompare + push "Wrong method name" + throw + @method_TestCompare: + meta method { + "name":"TestCompare","returnTpe":int8(9) + } + push null + push null + push null + push null + push null + push null + push null + meta source_mark { + "sl":int32(8),"sc":int32(8),"el":int32(8),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(18) + } + push int32(1) + push int32(8) + swapn + pop + meta source_mark { + "sl":int32(9),"sc":int32(8),"el":int32(9),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(18) + } + push int32(2) + push int32(7) + swapn + pop + meta source_mark { + "sl":int32(10),"sc":int32(8),"el":int32(10),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(20) + } + push int32(3) + push int32(6) + swapn + pop + meta source_mark { + "sl":int32(11),"sc":int32(8),"el":int32(11),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(20) + } + push int32(4) + push int32(5) + swapn + pop + meta source_mark { + "sl":int32(12),"sc":int32(8),"el":int32(12),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(20) + } + push int32(5) + push int8(5) + cast + push int32(4) + swapn + pop + meta source_mark { + "sl":int32(13),"sc":int32(8),"el":int32(13),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(20) + } + push int32(6) + push int8(5) + cast + push int32(3) + swapn + pop + meta source_mark { + "sl":int32(15),"sc":int32(8),"el":int32(15),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(24) + } + push int32(1) + push int32(2) + swapn + pop + meta source_mark { + "sl":int32(16),"sc":int32(8),"el":int32(16),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(22) + } + push int32(1) + dupn + push int32(8) + dupn + push int32(8) + dupn + eq + push int8(3) + cast + and + push int32(2) + swapn + pop + meta source_mark { + "sl":int32(17),"sc":int32(8),"el":int32(17),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(22) + } + push int32(1) + dupn + push int32(8) + dupn + push int32(7) + dupn + eq + push int8(3) + cast + and + push int32(2) + swapn + pop + meta source_mark { + "sl":int32(18),"sc":int32(8),"el":int32(18),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(22) + } + push int32(1) + dupn + push int32(6) + dupn + push int32(6) + dupn + eq + push int8(3) + cast + and + push int32(2) + swapn + pop + meta source_mark { + "sl":int32(19),"sc":int32(8),"el":int32(19),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(22) + } + push int32(1) + dupn + push int32(6) + dupn + push int8(5) + cast + push int32(5) + dupn + eq + push int8(3) + cast + and + push int32(2) + swapn + pop + meta source_mark { + "sl":int32(20),"sc":int32(8),"el":int32(20),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(22) + } + push int32(1) + dupn + push int32(4) + dupn + push int32(4) + dupn + eq + push int8(3) + cast + and + push int32(2) + swapn + pop + meta source_mark { + "sl":int32(22),"sc":int32(8),"el":int32(22),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(19) + } + push int32(7) + dupn + push int32(7) + dupn + eq + push int8(3) + cast + meta source_mark { + "sl":int32(22),"sc":int32(22),"el":int32(22),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(33) + } + push int8(9) + cast + not + push int8(3) + cast + push int32(0) + eq + not + jumpi @TestCompare_br75 + push int32(1) + meta source_mark { + "sl":int32(23),"sc":int32(8),"el":int32(23),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(19) + } + push int32(2) + swapn + pop + @TestCompare_br75: + push int32(7) + dupn + push int32(6) + dupn + meta source_mark { + "sl":int32(23),"sc":int32(22),"el":int32(23),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(33) + } + eq + push int8(3) + cast + push int8(9) + cast + not + push int8(3) + cast + push int32(0) + eq + not + jumpi @TestCompare_br82 + push int32(1) + meta source_mark { + "sl":int32(24),"sc":int32(8),"el":int32(24),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(19) + } + push int32(2) + swapn + pop + @TestCompare_br82: + push int32(5) + dupn + push int32(5) + dupn + meta source_mark { + "sl":int32(24),"sc":int32(22),"el":int32(24),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(33) + } + eq + push int8(3) + cast + push int8(9) + cast + not + push int8(3) + cast + push int32(0) + eq + not + jumpi @TestCompare_br89 + meta source_mark { + "sl":int32(25),"sc":int32(8),"el":int32(25),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(19) + } + push int32(1) + push int32(2) + swapn + pop + @TestCompare_br89: + push int32(5) + dupn + push int8(5) + cast + meta source_mark { + "sl":int32(25),"sc":int32(22),"el":int32(25),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(33) + } + push int32(4) + dupn + eq + push int8(3) + cast + meta source_mark { + "sl":int32(26),"sc":int32(8),"el":int32(26),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(19) + } + push int8(9) + cast + not + push int8(3) + cast + push int32(0) + eq + not + jumpi @TestCompare_br98 + push int32(1) + push int32(2) + swapn + pop + @TestCompare_br98: + push int32(3) + dupn + meta source_mark { + "sl":int32(26),"sc":int32(22),"el":int32(26),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(33) + } + push int32(3) + dupn + meta source_mark { + "sl":int32(28),"sc":int32(8),"el":int32(28),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(22) + } + eq + push int8(3) + cast + push int8(9) + cast + not + push int8(3) + cast + push int32(0) + eq + not + jumpi @TestCompare_br107 + push int32(1) + push int32(2) + swapn + pop + @TestCompare_br107: + push int32(1) + dupn + push int32(8) + dupn + push int32(8) + dupn + swap + lt + push int8(3) + cast + push int32(0) + meta source_mark { + "sl":int32(29),"sc":int32(8),"el":int32(29),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(22) + } + eq + push int8(3) + cast + and + push int32(2) + swapn + pop + push int32(1) + dupn + push int32(8) + dupn + push int32(7) + dupn + swap + lt + push int8(3) + cast + push int32(0) + meta source_mark { + "sl":int32(30),"sc":int32(8),"el":int32(30),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(22) + } + eq + push int8(3) + cast + and + push int32(2) + swapn + pop + push int32(1) + dupn + push int32(6) + dupn + push int32(6) + dupn + swap + lt + push int8(3) + cast + push int32(0) + meta source_mark { + "sl":int32(31),"sc":int32(8),"el":int32(31),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(22) + } + eq + push int8(3) + cast + and + push int32(2) + swapn + pop + push int32(1) + dupn + push int32(6) + dupn + push int8(5) + cast + push int32(5) + dupn + swap + lt + push int8(3) + cast + push int32(0) + meta source_mark { + "sl":int32(32),"sc":int32(8),"el":int32(32),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(22) + } + eq + push int8(3) + cast + and + push int32(2) + swapn + pop + push int32(1) + dupn + push int32(4) + dupn + push int32(4) + dupn + swap + lt + push int8(3) + cast + push int32(0) + meta source_mark { + "sl":int32(34),"sc":int32(8),"el":int32(34),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(19) + } + eq + push int8(3) + cast + and + meta source_mark { + "sl":int32(34),"sc":int32(22),"el":int32(34),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(33) + } + push int32(2) + swapn + pop + push int32(7) + dupn + push int32(7) + dupn + meta source_mark { + "sl":int32(35),"sc":int32(8),"el":int32(35),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(19) + } + swap + lt + push int8(3) + cast + push int32(0) + eq + not + jumpi @TestCompare_br178 + push int32(1) + meta source_mark { + "sl":int32(35),"sc":int32(22),"el":int32(35),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(33) + } + push int32(2) + swapn + pop + @TestCompare_br178: + push int32(7) + dupn + push int32(6) + dupn + meta source_mark { + "sl":int32(36),"sc":int32(8),"el":int32(36),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(19) + } + swap + lt + push int8(3) + cast + push int32(0) + eq + not + jumpi @TestCompare_br185 + push int32(1) + meta source_mark { + "sl":int32(36),"sc":int32(22),"el":int32(36),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(33) + } + push int32(2) + swapn + pop + @TestCompare_br185: + push int32(5) + dupn + push int32(5) + dupn + meta source_mark { + "sl":int32(37),"sc":int32(8),"el":int32(37),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(19) + } + swap + lt + push int8(3) + cast + push int32(0) + eq + not + jumpi @TestCompare_br192 + push int32(1) + push int32(2) + swapn + pop + @TestCompare_br192: + push int32(5) + dupn + meta source_mark { + "sl":int32(37),"sc":int32(22),"el":int32(37),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(33) + } + push int8(5) + cast + push int32(4) + dupn + meta source_mark { + "sl":int32(38),"sc":int32(8),"el":int32(38),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(19) + } + swap + lt + push int8(3) + cast + push int32(0) + eq + not + jumpi @TestCompare_br201 + push int32(1) + push int32(2) + swapn + pop + @TestCompare_br201: + meta source_mark { + "sl":int32(38),"sc":int32(22),"el":int32(38),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(33) + } + push int32(3) + dupn + push int32(3) + dupn + meta source_mark { + "sl":int32(40),"sc":int32(8),"el":int32(40),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(22) + } + swap + lt + push int8(3) + cast + push int32(0) + eq + not + jumpi @TestCompare_br210 + push int32(1) + push int32(2) + swapn + pop + @TestCompare_br210: + push int32(1) + dupn + push int32(8) + dupn + push int32(8) + dupn + swap + gt + push int8(3) + cast + push int32(0) + meta source_mark { + "sl":int32(41),"sc":int32(8),"el":int32(41),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(22) + } + eq + push int8(3) + cast + and + push int32(2) + swapn + pop + push int32(1) + dupn + push int32(8) + dupn + push int32(7) + dupn + swap + gt + push int8(3) + cast + push int32(0) + meta source_mark { + "sl":int32(42),"sc":int32(8),"el":int32(42),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(22) + } + eq + push int8(3) + cast + and + push int32(2) + swapn + pop + push int32(1) + dupn + push int32(6) + dupn + push int32(6) + dupn + swap + gt + push int8(3) + cast + push int32(0) + meta source_mark { + "sl":int32(43),"sc":int32(8),"el":int32(43),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(22) + } + eq + push int8(3) + cast + and + push int32(2) + swapn + pop + push int32(1) + dupn + push int32(6) + dupn + push int8(5) + cast + push int32(5) + dupn + swap + gt + push int8(3) + cast + push int32(0) + meta source_mark { + "sl":int32(44),"sc":int32(8),"el":int32(44),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(22) + } + eq + push int8(3) + cast + and + push int32(2) + swapn + pop + push int32(1) + dupn + push int32(4) + dupn + push int32(4) + dupn + swap + gt + push int8(3) + cast + push int32(0) + meta source_mark { + "sl":int32(46),"sc":int32(8),"el":int32(46),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(19) + } + eq + push int8(3) + cast + and + meta source_mark { + "sl":int32(46),"sc":int32(22),"el":int32(46),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(33) + } + push int32(2) + swapn + pop + push int32(7) + dupn + push int32(7) + dupn + meta source_mark { + "sl":int32(47),"sc":int32(8),"el":int32(47),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(19) + } + swap + gt + push int8(3) + cast + push int32(0) + eq + not + jumpi @TestCompare_br281 + push int32(1) + meta source_mark { + "sl":int32(47),"sc":int32(22),"el":int32(47),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(33) + } + push int32(2) + swapn + pop + @TestCompare_br281: + push int32(7) + dupn + push int32(6) + dupn + meta source_mark { + "sl":int32(48),"sc":int32(8),"el":int32(48),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(19) + } + swap + gt + push int8(3) + cast + push int32(0) + eq + not + jumpi @TestCompare_br288 + push int32(1) + meta source_mark { + "sl":int32(48),"sc":int32(22),"el":int32(48),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(33) + } + push int32(2) + swapn + pop + @TestCompare_br288: + push int32(5) + dupn + push int32(5) + dupn + meta source_mark { + "sl":int32(49),"sc":int32(8),"el":int32(49),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(19) + } + swap + gt + push int8(3) + cast + push int32(0) + eq + not + jumpi @TestCompare_br295 + push int32(1) + push int32(2) + swapn + pop + @TestCompare_br295: + push int32(5) + dupn + meta source_mark { + "sl":int32(49),"sc":int32(22),"el":int32(49),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(33) + } + push int8(5) + cast + push int32(4) + dupn + meta source_mark { + "sl":int32(50),"sc":int32(8),"el":int32(50),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(19) + } + swap + gt + push int8(3) + cast + push int32(0) + eq + not + jumpi @TestCompare_br304 + push int32(1) + push int32(2) + swapn + pop + @TestCompare_br304: + meta source_mark { + "sl":int32(50),"sc":int32(22),"el":int32(50),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(33) + } + push int32(3) + dupn + push int32(3) + dupn + meta source_mark { + "sl":int32(52),"sc":int32(8),"el":int32(52),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(22) + } + swap + gt + push int8(3) + cast + push int32(0) + eq + not + jumpi @TestCompare_br313 + push int32(1) + push int32(2) + swapn + pop + @TestCompare_br313: + push int32(1) + dupn + push int32(8) + dupn + push int32(8) + dupn + eq + push int8(3) + cast + push int32(0) + meta source_mark { + "sl":int32(53),"sc":int32(8),"el":int32(53),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(22) + } + eq + push int8(3) + cast + and + push int32(2) + swapn + pop + push int32(1) + dupn + push int32(8) + dupn + push int32(7) + dupn + eq + push int8(3) + cast + push int32(0) + meta source_mark { + "sl":int32(54),"sc":int32(8),"el":int32(54),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(22) + } + eq + push int8(3) + cast + and + push int32(2) + swapn + pop + push int32(1) + dupn + push int32(6) + dupn + push int32(6) + dupn + eq + push int8(3) + cast + push int32(0) + meta source_mark { + "sl":int32(55),"sc":int32(8),"el":int32(55),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(22) + } + eq + push int8(3) + cast + and + push int32(2) + swapn + pop + push int32(1) + dupn + push int32(6) + dupn + push int8(5) + cast + push int32(5) + dupn + eq + push int8(3) + cast + push int32(0) + meta source_mark { + "sl":int32(56),"sc":int32(8),"el":int32(56),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(22) + } + eq + push int8(3) + cast + and + push int32(2) + swapn + pop + push int32(1) + dupn + push int32(4) + dupn + push int32(4) + dupn + eq + push int8(3) + cast + push int32(0) + meta source_mark { + "sl":int32(58),"sc":int32(8),"el":int32(58),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(19) + } + eq + push int8(3) + cast + and + meta source_mark { + "sl":int32(58),"sc":int32(22),"el":int32(58),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(33) + } + push int32(2) + swapn + pop + push int32(7) + dupn + push int32(7) + dupn + meta source_mark { + "sl":int32(59),"sc":int32(8),"el":int32(59),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(19) + } + eq + push int8(3) + cast + push int32(0) + eq + not + jumpi @TestCompare_br384 + push int32(1) + meta source_mark { + "sl":int32(59),"sc":int32(22),"el":int32(59),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(33) + } + push int32(2) + swapn + pop + @TestCompare_br384: + push int32(7) + dupn + push int32(6) + dupn + meta source_mark { + "sl":int32(60),"sc":int32(8),"el":int32(60),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(19) + } + eq + push int8(3) + cast + push int32(0) + eq + not + jumpi @TestCompare_br391 + push int32(1) + meta source_mark { + "sl":int32(60),"sc":int32(22),"el":int32(60),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(33) + } + push int32(2) + swapn + pop + @TestCompare_br391: + push int32(5) + dupn + push int32(5) + dupn + meta source_mark { + "sl":int32(61),"sc":int32(8),"el":int32(61),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(19) + } + eq + push int8(3) + cast + push int32(0) + eq + not + jumpi @TestCompare_br398 + push int32(1) + push int32(2) + swapn + pop + @TestCompare_br398: + push int32(5) + dupn + meta source_mark { + "sl":int32(61),"sc":int32(22),"el":int32(61),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(33) + } + push int8(5) + cast + push int32(4) + dupn + meta source_mark { + "sl":int32(62),"sc":int32(8),"el":int32(62),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(19) + } + eq + push int8(3) + cast + push int32(0) + eq + not + jumpi @TestCompare_br407 + push int32(1) + push int32(2) + swapn + pop + @TestCompare_br407: + meta source_mark { + "sl":int32(62),"sc":int32(22),"el":int32(62),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(33) + } + push int32(3) + dupn + push int32(3) + dupn + meta source_mark { + "sl":int32(64),"sc":int32(8),"el":int32(64),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(21) + } + eq + push int8(3) + cast + push int32(0) + eq + not + jumpi @TestCompare_br416 + push int32(1) + push int32(2) + swapn + pop + @TestCompare_br416: + push int32(1) + dupn + push int32(8) + dupn + push int32(8) + dupn + meta source_mark { + "sl":int32(65),"sc":int32(8),"el":int32(65),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(21) + } + swap + gt + push int8(3) + cast + and + push int32(2) + swapn + pop + push int32(1) + dupn + push int32(8) + dupn + push int32(7) + dupn + meta source_mark { + "sl":int32(66),"sc":int32(8),"el":int32(66),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(21) + } + swap + gt + push int8(3) + cast + and + push int32(2) + swapn + pop + push int32(1) + dupn + push int32(6) + dupn + push int32(6) + dupn + meta source_mark { + "sl":int32(67),"sc":int32(8),"el":int32(67),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(21) + } + swap + gt + push int8(3) + cast + and + push int32(2) + swapn + pop + push int32(1) + dupn + push int32(6) + dupn + push int8(5) + cast + push int32(5) + dupn + meta source_mark { + "sl":int32(68),"sc":int32(8),"el":int32(68),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(21) + } + swap + gt + push int8(3) + cast + and + push int32(2) + swapn + pop + push int32(1) + dupn + push int32(4) + dupn + push int32(4) + dupn + meta source_mark { + "sl":int32(70),"sc":int32(8),"el":int32(70),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(18) + } + swap + gt + push int8(3) + cast + and + meta source_mark { + "sl":int32(70),"sc":int32(21),"el":int32(70),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(32) + } + push int32(2) + swapn + pop + push int32(7) + dupn + push int32(7) + dupn + meta source_mark { + "sl":int32(71),"sc":int32(8),"el":int32(71),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(18) + } + swap + gt + push int8(3) + cast + push int8(9) + cast + not + push int8(3) + cast + push int32(0) + eq + not + jumpi @TestCompare_br472 + push int32(1) + meta source_mark { + "sl":int32(71),"sc":int32(21),"el":int32(71),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(32) + } + push int32(2) + swapn + pop + @TestCompare_br472: + push int32(7) + dupn + meta source_mark { + "sl":int32(72),"sc":int32(8),"el":int32(72),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(18) + } + push int32(6) + dupn + swap + gt + push int8(3) + cast + push int8(9) + cast + not + push int8(3) + cast + push int32(0) + eq + not + jumpi @TestCompare_br479 + meta source_mark { + "sl":int32(72),"sc":int32(21),"el":int32(72),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(32) + } + push int32(1) + push int32(2) + swapn + pop + @TestCompare_br479: + meta source_mark { + "sl":int32(73),"sc":int32(8),"el":int32(73),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(18) + } + push int32(5) + dupn + push int32(5) + dupn + swap + gt + push int8(3) + cast + push int8(9) + cast + not + push int8(3) + cast + push int32(0) + eq + not + jumpi @TestCompare_br486 + push int32(1) + meta source_mark { + "sl":int32(73),"sc":int32(21),"el":int32(73),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(32) + } + push int32(2) + swapn + pop + @TestCompare_br486: + push int32(5) + dupn + meta source_mark { + "sl":int32(74),"sc":int32(8),"el":int32(74),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(18) + } + push int8(5) + cast + push int32(4) + dupn + swap + gt + push int8(3) + cast + push int8(9) + cast + not + push int8(3) + cast + push int32(0) + eq + not + jumpi @TestCompare_br495 + meta source_mark { + "sl":int32(74),"sc":int32(21),"el":int32(74),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(32) + } + push int32(1) + push int32(2) + swapn + pop + @TestCompare_br495: + meta source_mark { + "sl":int32(76),"sc":int32(8),"el":int32(76),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(21) + } + push int32(3) + dupn + push int32(3) + dupn + swap + gt + push int8(3) + cast + push int8(9) + cast + not + push int8(3) + cast + push int32(0) + eq + not + jumpi @TestCompare_br504 + push int32(1) + meta source_mark { + "sl":int32(77),"sc":int32(8),"el":int32(77),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(21) + } + push int32(2) + swapn + pop + @TestCompare_br504: + push int32(1) + dupn + push int32(8) + dupn + push int32(8) + dupn + swap + lt + push int8(3) + cast + and + meta source_mark { + "sl":int32(78),"sc":int32(8),"el":int32(78),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(21) + } + push int32(2) + swapn + pop + push int32(1) + dupn + push int32(8) + dupn + push int32(7) + dupn + swap + lt + push int8(3) + cast + and + meta source_mark { + "sl":int32(79),"sc":int32(8),"el":int32(79),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(21) + } + push int32(2) + swapn + pop + push int32(1) + dupn + push int32(6) + dupn + push int32(6) + dupn + swap + lt + push int8(3) + cast + and + push int32(2) + swapn + pop + meta source_mark { + "sl":int32(80),"sc":int32(8),"el":int32(80),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(21) + } + push int32(1) + dupn + push int32(6) + dupn + push int8(5) + cast + push int32(5) + dupn + swap + lt + push int8(3) + cast + and + push int32(2) + swapn + pop + meta source_mark { + "sl":int32(82),"sc":int32(8),"el":int32(82),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(18) + } + push int32(1) + dupn + push int32(4) + dupn + meta source_mark { + "sl":int32(82),"sc":int32(21),"el":int32(82),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(32) + } + push int32(4) + dupn + swap + lt + push int8(3) + cast + meta source_mark { + "sl":int32(83),"sc":int32(8),"el":int32(83),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(18) + } + and + push int32(2) + swapn + pop + push int32(7) + dupn + meta source_mark { + "sl":int32(83),"sc":int32(21),"el":int32(83),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(32) + } + push int32(7) + dupn + swap + lt + push int8(3) + cast + meta source_mark { + "sl":int32(84),"sc":int32(8),"el":int32(84),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(18) + } + push int8(9) + cast + not + push int8(3) + cast + push int32(0) + eq + not + jumpi @TestCompare_br560 + push int32(1) + push int32(2) + swapn + pop + @TestCompare_br560: + meta source_mark { + "sl":int32(84),"sc":int32(21),"el":int32(84),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(32) + } + push int32(7) + dupn + push int32(6) + dupn + meta source_mark { + "sl":int32(85),"sc":int32(8),"el":int32(85),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(18) + } + swap + lt + push int8(3) + cast + push int8(9) + cast + not + push int8(3) + cast + push int32(0) + eq + not + jumpi @TestCompare_br567 + push int32(1) + push int32(2) + swapn + pop + @TestCompare_br567: + push int32(5) + dupn + meta source_mark { + "sl":int32(85),"sc":int32(21),"el":int32(85),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(32) + } + push int32(5) + dupn + swap + lt + push int8(3) + cast + meta source_mark { + "sl":int32(86),"sc":int32(8),"el":int32(86),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(18) + } + push int8(9) + cast + not + push int8(3) + cast + push int32(0) + eq + not + jumpi @TestCompare_br574 + push int32(1) + push int32(2) + swapn + pop + @TestCompare_br574: + push int32(5) + dupn + push int8(5) + cast + meta source_mark { + "sl":int32(86),"sc":int32(21),"el":int32(86),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(32) + } + push int32(4) + dupn + meta source_mark { + "sl":int32(88),"sc":int32(8),"el":int32(88),"src":"$PRAVDA_TMP_DIR/Compare.cs","ec":int32(19) + } + swap + lt + push int8(3) + cast + push int8(9) + cast + not + push int8(3) + cast + push int32(0) + eq + not + jumpi @TestCompare_br583 + push int32(1) + push int32(2) + swapn + pop + @TestCompare_br583: + push int32(3) + dupn + push int32(3) + dupn + swap + lt + push int8(3) + cast + push int8(9) + cast + not + push int8(3) + cast + push int32(0) + eq + not + jumpi @TestCompare_br592 + push int32(1) + push int32(2) + swapn + pop + @TestCompare_br592: + push int32(1) + dupn + jump @TestCompare_lvc + @TestCompare_lvc: + swap + pop + swap + pop + swap + pop + swap + pop + swap + pop + swap + pop + swap + pop + swap + pop + jump @stop + @method_ctor: + meta method { + "name":"ctor","returnTpe":int8(0) + } + push "init" + sexist + not + jumpi @ctor_ok + push "Program has been already initialized" + throw + @ctor_ok: + push null + push "init" + sput + jump @ctor_lvc + @ctor_lvc: + pop + jump @stop + @stop: diff --git a/dotnet/src/test/resources/translation/ConcatStrings.trs b/dotnet/src/test/resources/translation/ConcatStrings.trs new file mode 100644 index 00000000..f07e404b --- /dev/null +++ b/dotnet/src/test/resources/translation/ConcatStrings.trs @@ -0,0 +1,620 @@ +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: ConcatStrings.exe + sources: + - Pravda.dll + - dotnet-tests/resources/ConcatStrings.cs + optimize: true +--- +translation: |- + meta custom "CIL" + meta program_name "ConcatStrings" + dup + push "ctor" + eq + jumpi @method_ctor + push "init" + sexist + jumpi @methods + push "Program was not initialized" + throw + @methods: + dup + push "TestConcatStrings" + eq + jumpi @method_TestConcatStrings + push "Wrong method name" + throw + @method_TestConcatStrings: + meta method { + "name":"TestConcatStrings","returnTpe":int8(11) + } + push null + push null + push null + push null + push null + push null + push null + push null + push null + push null + meta source_mark { + "sl":int32(9),"sc":int32(9),"el":int32(9),"src":"$PRAVDA_TMP_DIR/ConcatStrings.cs","ec":int32(24) + } + push "s" + push int32(11) + swapn + pop + meta source_mark { + "sl":int32(10),"sc":int32(9),"el":int32(10),"src":"$PRAVDA_TMP_DIR/ConcatStrings.cs","ec":int32(33) + } + push int32(10) + dupn + push int32(11) + dupn + push "2" + swap + concat + swap + concat + push int32(10) + swapn + pop + meta source_mark { + "sl":int32(11),"sc":int32(9),"el":int32(11),"src":"$PRAVDA_TMP_DIR/ConcatStrings.cs","ec":int32(37) + } + push int32(10) + dupn + push int32(11) + dupn + push int32(12) + dupn + push "3" + swap + concat + swap + concat + swap + concat + push int32(9) + swapn + pop + meta source_mark { + "sl":int32(12),"sc":int32(9),"el":int32(12),"src":"$PRAVDA_TMP_DIR/ConcatStrings.cs","ec":int32(41) + } + push int32(5) + push int8(11) + new_array + dup + push int32(0) + push int32(13) + dupn + swap + array_mut + dup + push int32(1) + push int32(13) + dupn + swap + array_mut + dup + push int32(2) + push int32(13) + dupn + swap + array_mut + dup + push int32(3) + push int32(13) + dupn + swap + array_mut + dup + push int32(4) + push "4" + swap + array_mut + call @stdlib_concat_all_string + push int32(8) + swapn + pop + meta source_mark { + "sl":int32(13),"sc":int32(9),"el":int32(13),"src":"$PRAVDA_TMP_DIR/ConcatStrings.cs","ec":int32(45) + } + push int32(6) + push int8(11) + new_array + dup + push int32(0) + push int32(13) + dupn + swap + array_mut + dup + push int32(1) + push int32(13) + dupn + swap + array_mut + dup + push int32(2) + push int32(13) + dupn + swap + array_mut + dup + push int32(3) + push int32(13) + dupn + swap + array_mut + dup + push int32(4) + push int32(13) + dupn + swap + array_mut + dup + push int32(5) + push "5" + swap + array_mut + call @stdlib_concat_all_string + push int32(7) + swapn + pop + meta source_mark { + "sl":int32(14),"sc":int32(9),"el":int32(14),"src":"$PRAVDA_TMP_DIR/ConcatStrings.cs","ec":int32(49) + } + push int32(7) + push int8(11) + new_array + dup + push int32(0) + push int32(13) + dupn + swap + array_mut + dup + push int32(1) + push int32(13) + dupn + swap + array_mut + dup + push int32(2) + push int32(13) + dupn + swap + array_mut + dup + push int32(3) + push int32(13) + dupn + swap + array_mut + dup + push int32(4) + push int32(13) + dupn + swap + array_mut + dup + push int32(5) + push int32(13) + dupn + swap + array_mut + dup + push int32(6) + push "6" + swap + array_mut + call @stdlib_concat_all_string + push int32(6) + swapn + pop + meta source_mark { + "sl":int32(15),"sc":int32(9),"el":int32(15),"src":"$PRAVDA_TMP_DIR/ConcatStrings.cs","ec":int32(53) + } + push int32(8) + push int8(11) + new_array + dup + push int32(0) + push int32(13) + dupn + swap + array_mut + dup + push int32(1) + push int32(13) + dupn + swap + array_mut + dup + push int32(2) + push int32(13) + dupn + swap + array_mut + dup + push int32(3) + push int32(13) + dupn + swap + array_mut + dup + push int32(4) + push int32(13) + dupn + swap + array_mut + dup + push int32(5) + push int32(13) + dupn + swap + array_mut + dup + push int32(6) + push int32(13) + dupn + swap + array_mut + dup + push int32(7) + push "7" + swap + array_mut + call @stdlib_concat_all_string + push int32(5) + swapn + pop + meta source_mark { + "sl":int32(16),"sc":int32(9),"el":int32(16),"src":"$PRAVDA_TMP_DIR/ConcatStrings.cs","ec":int32(57) + } + push int32(9) + push int8(11) + new_array + dup + push int32(0) + push int32(13) + dupn + swap + array_mut + dup + push int32(1) + push int32(13) + dupn + swap + array_mut + dup + push int32(2) + push int32(13) + dupn + swap + array_mut + dup + push int32(3) + push int32(13) + dupn + swap + array_mut + dup + push int32(4) + push int32(13) + dupn + swap + array_mut + dup + push int32(5) + push int32(13) + dupn + swap + array_mut + dup + push int32(6) + push int32(13) + dupn + swap + array_mut + dup + push int32(7) + push int32(13) + dupn + swap + array_mut + dup + push int32(8) + push "8" + swap + array_mut + call @stdlib_concat_all_string + push int32(4) + swapn + pop + meta source_mark { + "sl":int32(17),"sc":int32(9),"el":int32(17),"src":"$PRAVDA_TMP_DIR/ConcatStrings.cs","ec":int32(61) + } + push int32(10) + push int8(11) + new_array + dup + push int32(0) + push int32(13) + dupn + swap + array_mut + dup + push int32(1) + push int32(13) + dupn + swap + array_mut + dup + push int32(2) + push int32(13) + dupn + swap + array_mut + dup + push int32(3) + push int32(13) + dupn + swap + array_mut + dup + push int32(4) + push int32(13) + dupn + swap + array_mut + dup + push int32(5) + push int32(13) + dupn + swap + array_mut + dup + push int32(6) + push int32(13) + dupn + swap + array_mut + dup + push int32(7) + push int32(13) + dupn + swap + array_mut + dup + push int32(8) + push int32(13) + dupn + swap + array_mut + dup + push int32(9) + push "9" + swap + array_mut + call @stdlib_concat_all_string + push int32(3) + swapn + pop + meta source_mark { + "sl":int32(18),"sc":int32(9),"el":int32(18),"src":"$PRAVDA_TMP_DIR/ConcatStrings.cs","ec":int32(67) + } + push int32(11) + push int8(11) + new_array + dup + push int32(0) + push int32(13) + dupn + swap + array_mut + dup + push int32(1) + push int32(13) + dupn + swap + array_mut + dup + push int32(2) + push int32(13) + dupn + swap + array_mut + dup + push int32(3) + push int32(13) + dupn + swap + array_mut + dup + push int32(4) + push int32(13) + dupn + swap + array_mut + dup + push int32(5) + push int32(13) + dupn + swap + array_mut + dup + push int32(6) + push int32(13) + dupn + swap + array_mut + dup + push int32(7) + push int32(13) + dupn + swap + array_mut + dup + push int32(8) + push int32(13) + dupn + swap + array_mut + dup + push int32(9) + push int32(13) + dupn + swap + array_mut + dup + push int32(10) + push "10" + swap + array_mut + call @stdlib_concat_all_string + push int32(2) + swapn + pop + meta source_mark { + "sl":int32(20),"sc":int32(9),"el":int32(20),"src":"$PRAVDA_TMP_DIR/ConcatStrings.cs","ec":int32(60) + } + push int32(9) + push int8(11) + new_array + dup + push int32(0) + push int32(12) + dupn + swap + array_mut + dup + push int32(1) + push int32(11) + dupn + swap + array_mut + dup + push int32(2) + push int32(10) + dupn + swap + array_mut + dup + push int32(3) + push int32(9) + dupn + swap + array_mut + dup + push int32(4) + push int32(8) + dupn + swap + array_mut + dup + push int32(5) + push int32(7) + dupn + swap + array_mut + dup + push int32(6) + push int32(6) + dupn + swap + array_mut + dup + push int32(7) + push int32(5) + dupn + swap + array_mut + dup + push int32(8) + push int32(4) + dupn + swap + array_mut + call @stdlib_concat_all_string + jump @TestConcatStrings_lvc + @TestConcatStrings_lvc: + swap + pop + swap + pop + swap + pop + swap + pop + swap + pop + swap + pop + swap + pop + swap + pop + swap + pop + swap + pop + swap + pop + jump @stop + @method_ctor: + meta method { + "name":"ctor","returnTpe":int8(0) + } + push "init" + sexist + not + jumpi @ctor_ok + push "Program has been already initialized" + throw + @ctor_ok: + push null + push "init" + sput + jump @ctor_lvc + @ctor_lvc: + pop + jump @stop + @stdlib_concat_all_string: + dup + length + push "" + push int32(0) + @concat_all_string_loop: + push int32(4) + dupn + push int32(2) + dupn + array_get + push int32(3) + dupn + concat + push int32(3) + swapn + pop + push int32(1) + add + dup + push int32(4) + dupn + gt + jumpi @concat_all_string_loop + pop + swap + pop + swap + pop + ret + @stop: diff --git a/dotnet/src/test/resources/translation/event.trs b/dotnet/src/test/resources/translation/Event.trs similarity index 53% rename from dotnet/src/test/resources/translation/event.trs rename to dotnet/src/test/resources/translation/Event.trs index 7c9a52fb..b7f7bbda 100644 --- a/dotnet/src/test/resources/translation/event.trs +++ b/dotnet/src/test/resources/translation/Event.trs @@ -1,7 +1,18 @@ -exe: event.exe +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: Event.exe + sources: + - Pravda.dll + - dotnet-tests/resources/Event.cs + optimize: true --- translation: |- meta custom "CIL" + meta program_name "Event" dup push "ctor" eq @@ -13,34 +24,49 @@ translation: |- throw @methods: dup - push "MakeEvent" + push "TestEvent" eq - jumpi @method_MakeEvent + jumpi @method_TestEvent push "Wrong method name" throw - @method_MakeEvent: + @method_TestEvent: meta method { - "name":"MakeEvent","returnTpe":int8(0) + "name":"TestEvent","returnTpe":int8(0) + } + meta source_mark { + "sl":int32(8),"sc":int32(9),"el":int32(8),"src":"$PRAVDA_TMP_DIR/Event.cs","ec":int32(37) } push "my_event" push int32(1234) swap event + meta source_mark { + "sl":int32(9),"sc":int32(9),"el":int32(9),"src":"$PRAVDA_TMP_DIR/Event.cs","ec":int32(44) + } push "my_event" push "my_string" swap event + meta source_mark { + "sl":int32(10),"sc":int32(9),"el":int32(10),"src":"$PRAVDA_TMP_DIR/Event.cs","ec":int32(36) + } push "my_event" push number(2.0) swap event + meta source_mark { + "sl":int32(11),"sc":int32(9),"el":int32(11),"src":"$PRAVDA_TMP_DIR/Event.cs","ec":int32(66) + } push "my_event" new int8[1, 2, 3, 4] call @stdlib_array_to_bytes swap event - jump @MakeEvent_lvc - @MakeEvent_lvc: + meta source_mark { + "sl":int32(12),"sc":int32(5),"el":int32(12),"src":"$PRAVDA_TMP_DIR/Event.cs","ec":int32(6) + } + jump @TestEvent_lvc + @TestEvent_lvc: pop jump @stop @method_ctor: diff --git a/dotnet/src/test/resources/translation/ExternalMethods.trs b/dotnet/src/test/resources/translation/ExternalMethods.trs new file mode 100644 index 00000000..cb570d8b --- /dev/null +++ b/dotnet/src/test/resources/translation/ExternalMethods.trs @@ -0,0 +1,325 @@ +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: ExternalMethods.dll + sources: + - Pravda.dll + - dotnet-tests/resources/ExternalMethods.cs + optimize: true + - target: ExternalMethodsCheck.exe + sources: + - Pravda.dll + - ExternalMethods.dll + - dotnet-tests/resources/ExternalMethodsCheck.cs + optimize: true + main-class: ExternalNamespace.ExternalMethodsCheck +--- +translation: |- + meta custom "CIL" + meta program_name "ExternalNamespaceExternalMethodsCheck" + dup + push "ctor" + eq + jumpi @method_ctor + push "init" + sexist + jumpi @methods + push "Program was not initialized" + throw + @methods: + dup + push "TestRegularMethods" + eq + jumpi @method_TestRegularMethods + dup + push "TestStaticMethods" + eq + jumpi @method_TestStaticMethods + push "Wrong method name" + throw + @method_TestRegularMethods: + meta method { + "name":"TestRegularMethods","returnTpe":int8(3) + } + push null + push null + push null + meta source_mark { + "sl":int32(21),"sc":int32(13),"el":int32(21),"src":"$PRAVDA_TMP_DIR/ExternalMethodsCheck.cs","ec":int32(60) + } + push int32(3) + push int32(3) + new {} + call @vtable_ExternalNamespace.ExternalMethods + call @default_fields_ExternalNamespace.ExternalMethods + push int32(3) + swapn + push int32(2) + swapn + call @func_ExternalNamespace.ExternalMethods.ctor_int32_int32 + meta source_mark { + "sl":int32(22),"sc":int32(13),"el":int32(22),"src":"$PRAVDA_TMP_DIR/ExternalMethodsCheck.cs","ec":int32(30) + } + dup + call @func_ExternalNamespace.ExternalMethods.Add + swap + pop + push int32(5) + swapn + pop + meta source_mark { + "sl":int32(23),"sc":int32(13),"el":int32(23),"src":"$PRAVDA_TMP_DIR/ExternalMethodsCheck.cs","ec":int32(33) + } + push int32(100) + call @func_ExternalNamespace.ExternalMethods.Add_int32 + swap + pop + push int32(3) + swapn + pop + meta source_mark { + "sl":int32(24),"sc":int32(13),"el":int32(24),"src":"$PRAVDA_TMP_DIR/ExternalMethodsCheck.cs","ec":int32(53) + } + push int32(1000) + push int32(1000) + call @func_ExternalNamespace.ExternalMethods.Add_int32_int32 + push int32(2) + swapn + pop + meta source_mark { + "sl":int32(26),"sc":int32(13),"el":int32(26),"src":"$PRAVDA_TMP_DIR/ExternalMethodsCheck.cs","ec":int32(30) + } + push int32(3) + dupn + push int32(3) + dupn + add + push int32(2) + dupn + add + jump @TestRegularMethods_lvc + @TestRegularMethods_lvc: + swap + pop + swap + pop + swap + pop + swap + pop + jump @stop + @method_TestStaticMethods: + meta method { + "name":"TestStaticMethods","returnTpe":int8(3) + } + push null + push null + push null + meta source_mark { + "sl":int32(11),"sc":int32(13),"el":int32(11),"src":"$PRAVDA_TMP_DIR/ExternalMethodsCheck.cs","ec":int32(79) + } + call @func_ExternalNamespace.ExternalProgramMethods.GetInstance + meta source_mark { + "sl":int32(12),"sc":int32(13),"el":int32(12),"src":"$PRAVDA_TMP_DIR/ExternalMethodsCheck.cs","ec":int32(35) + } + dup + push int32(2) + push int32(2) + push int32(2) + swapn + push int32(3) + swapn + push "Add" + swap + push int32(3) + pcall + push int32(5) + swapn + pop + meta source_mark { + "sl":int32(13),"sc":int32(13),"el":int32(13),"src":"$PRAVDA_TMP_DIR/ExternalMethodsCheck.cs","ec":int32(37) + } + dup + push int32(10) + push int32(10) + push int32(2) + swapn + push int32(3) + swapn + push "Add" + swap + push int32(3) + pcall + push int32(4) + swapn + pop + meta source_mark { + "sl":int32(14),"sc":int32(13),"el":int32(14),"src":"$PRAVDA_TMP_DIR/ExternalMethodsCheck.cs","ec":int32(39) + } + push int32(300) + push int32(300) + push int32(2) + swapn + push int32(3) + swapn + push "Add" + swap + push int32(3) + pcall + push int32(2) + swapn + pop + meta source_mark { + "sl":int32(16),"sc":int32(13),"el":int32(16),"src":"$PRAVDA_TMP_DIR/ExternalMethodsCheck.cs","ec":int32(30) + } + push int32(3) + dupn + push int32(3) + dupn + add + push int32(2) + dupn + add + jump @TestStaticMethods_lvc + @TestStaticMethods_lvc: + swap + pop + swap + pop + swap + pop + swap + pop + jump @stop + @method_ctor: + meta method { + "name":"ctor","returnTpe":int8(0) + } + push "init" + sexist + not + jumpi @ctor_ok + push "Program has been already initialized" + throw + @ctor_ok: + push null + push "init" + sput + jump @ctor_lvc + @ctor_lvc: + pop + jump @stop + @default_fields_ExternalNamespace.ExternalMethods: + dup + push int32(0) + struct_mut "A" + dup + push int32(0) + struct_mut "B" + ret + @default_fields_ExternalNamespace.ExternalMethods: + dup + push null + struct_mut "A" + dup + push null + struct_mut "B" + ret + @func_ExternalNamespace.ExternalMethods.Add: + meta source_mark { + "sl":int32(20),"sc":int32(13),"el":int32(20),"src":"$PRAVDA_TMP_DIR/ExternalMethods.cs","ec":int32(26) + } + push int32(1) + dupn + struct_get "A" + push int32(2) + dupn + struct_get "B" + add + jump @ExternalNamespace.ExternalMethods.Add_lvc + @ExternalNamespace.ExternalMethods.Add_lvc: + ret + @func_ExternalNamespace.ExternalMethods.Add_int32: + meta source_mark { + "sl":int32(24),"sc":int32(13),"el":int32(24),"src":"$PRAVDA_TMP_DIR/ExternalMethods.cs","ec":int32(30) + } + push int32(2) + dupn + struct_get "A" + push int32(3) + dupn + struct_get "B" + add + push int32(2) + dupn + add + jump @ExternalNamespace.ExternalMethods.Add_int32_lvc + @ExternalNamespace.ExternalMethods.Add_int32_lvc: + swap + pop + ret + @func_ExternalNamespace.ExternalMethods.Add_int32_int32: + meta source_mark { + "sl":int32(11),"sc":int32(13),"el":int32(11),"src":"$PRAVDA_TMP_DIR/ExternalMethods.cs","ec":int32(26) + } + push int32(2) + dupn + push int32(2) + dupn + add + jump @ExternalNamespace.ExternalMethods.Add_int32_int32_lvc + @ExternalNamespace.ExternalMethods.Add_int32_int32_lvc: + swap + pop + swap + pop + ret + @func_ExternalNamespace.ExternalMethods.ctor_int32_int32: + meta source_mark { + "sl":int32(14),"sc":int32(9),"el":int32(14),"src":"$PRAVDA_TMP_DIR/ExternalMethods.cs","ec":int32(45) + } + push int32(3) + dupn + pop + meta source_mark { + "sl":int32(15),"sc":int32(13),"el":int32(15),"src":"$PRAVDA_TMP_DIR/ExternalMethods.cs","ec":int32(24) + } + push int32(3) + dupn + push int32(3) + dupn + struct_mut "A" + meta source_mark { + "sl":int32(16),"sc":int32(13),"el":int32(16),"src":"$PRAVDA_TMP_DIR/ExternalMethods.cs","ec":int32(24) + } + push int32(3) + dupn + push int32(2) + dupn + struct_mut "B" + meta source_mark { + "sl":int32(17),"sc":int32(9),"el":int32(17),"src":"$PRAVDA_TMP_DIR/ExternalMethods.cs","ec":int32(10) + } + jump @ExternalNamespace.ExternalMethods.ctor_int32_int32_lvc + @ExternalNamespace.ExternalMethods.ctor_int32_int32_lvc: + pop + pop + ret + @func_ExternalNamespace.ExternalProgramMethods.GetInstance: + meta source_mark { + "sl":int32(33),"sc":int32(13),"el":int32(35),"src":"$PRAVDA_TMP_DIR/ExternalMethods.cs","ec":int32(16) + } + push "123456789012345678901234567890123456789012345678901234567890ABCD" + push int32(4) + scall + jump @ExternalNamespace.ExternalProgramMethods.GetInstance_lvc + @ExternalNamespace.ExternalProgramMethods.GetInstance_lvc: + ret + @vtable_ExternalNamespace.ExternalMethods: + ret + @vtable_ExternalNamespace.ExternalMethods: + ret + @stop: diff --git a/dotnet/src/test/resources/translation/If.trs b/dotnet/src/test/resources/translation/If.trs new file mode 100644 index 00000000..036c33ba --- /dev/null +++ b/dotnet/src/test/resources/translation/If.trs @@ -0,0 +1,355 @@ +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: If.exe + sources: + - Pravda.dll + - dotnet-tests/resources/If.cs + optimize: true +--- +translation: |- + meta custom "CIL" + meta program_name "If" + dup + push "ctor" + eq + jumpi @method_ctor + push "init" + sexist + jumpi @methods + push "Program was not initialized" + throw + @methods: + dup + push "TestIfs" + eq + jumpi @method_TestIfs + push "Wrong method name" + throw + @method_TestIfs: + meta method { + "name":"TestIfs","returnTpe":int8(0) + } + push null + meta source_mark { + "sl":int32(8),"sc":int32(8),"el":int32(8),"src":"$PRAVDA_TMP_DIR/If.cs","ec":int32(19) + } + push int32(10) + push int32(2) + swapn + pop + meta source_mark { + "sl":int32(10),"sc":int32(8),"el":int32(10),"src":"$PRAVDA_TMP_DIR/If.cs","ec":int32(18) + } + push int32(1) + dupn + push int32(1) + swap + lt + push int8(3) + cast + meta source_mark { + "sl":int32(11),"sc":int32(12),"el":int32(11),"src":"$PRAVDA_TMP_DIR/If.cs","ec":int32(18) + } + push int8(9) + cast + not + push int8(3) + cast + push int32(0) + eq + not + jumpi @TestIfs_br9 + push int32(4) + meta source_mark { + "sl":int32(14),"sc":int32(8),"el":int32(14),"src":"$PRAVDA_TMP_DIR/If.cs","ec":int32(18) + } + push int32(2) + swapn + pop + @TestIfs_br9: + push int32(1) + dupn + push int32(5) + meta source_mark { + "sl":int32(15),"sc":int32(11),"el":int32(15),"src":"$PRAVDA_TMP_DIR/If.cs","ec":int32(21) + } + swap + gt + push int8(3) + cast + push int8(9) + cast + not + push int8(3) + cast + push int32(0) + eq + not + jumpi @TestIfs_br19 + push int32(1) + dupn + push int32(6) + meta source_mark { + "sl":int32(16),"sc":int32(15),"el":int32(16),"src":"$PRAVDA_TMP_DIR/If.cs","ec":int32(21) + } + swap + gt + push int8(3) + cast + meta source_mark { + "sl":int32(20),"sc":int32(8),"el":int32(20),"src":"$PRAVDA_TMP_DIR/If.cs","ec":int32(18) + } + push int8(9) + cast + not + push int8(3) + cast + push int32(0) + eq + not + jumpi @TestIfs_br19 + push int32(7) + push int32(2) + swapn + pop + @TestIfs_br19: + push int32(1) + dupn + meta source_mark { + "sl":int32(21),"sc":int32(11),"el":int32(21),"src":"$PRAVDA_TMP_DIR/If.cs","ec":int32(17) + } + push int32(0) + meta source_mark { + "sl":int32(22),"sc":int32(8),"el":int32(22),"src":"$PRAVDA_TMP_DIR/If.cs","ec":int32(9) + } + swap + gt + push int8(3) + cast + push int8(9) + cast + not + push int8(3) + cast + push int32(0) + eq + not + jumpi @TestIfs_br27 + meta source_mark { + "sl":int32(23),"sc":int32(11),"el":int32(23),"src":"$PRAVDA_TMP_DIR/If.cs","ec":int32(17) + } + push int32(4) + push int32(2) + swapn + pop + jump @TestIfs_br29 + @TestIfs_br27: + meta source_mark { + "sl":int32(26),"sc":int32(8),"el":int32(26),"src":"$PRAVDA_TMP_DIR/If.cs","ec":int32(27) + } + push int32(5) + push int32(2) + swapn + pop + @TestIfs_br29: + push int32(1) + dupn + push int32(2) + swap + gt + push int8(3) + cast + push int8(9) + cast + not + push int8(3) + cast + push int32(0) + eq + not + jumpi @TestIfs_br41 + push int32(1) + dupn + meta source_mark { + "sl":int32(27),"sc":int32(11),"el":int32(27),"src":"$PRAVDA_TMP_DIR/If.cs","ec":int32(17) + } + push int32(4) + meta source_mark { + "sl":int32(28),"sc":int32(8),"el":int32(28),"src":"$PRAVDA_TMP_DIR/If.cs","ec":int32(9) + } + swap + lt + push int8(3) + cast + push int8(9) + cast + not + push int8(3) + cast + push int32(0) + eq + not + jumpi @TestIfs_br41 + meta source_mark { + "sl":int32(29),"sc":int32(11),"el":int32(29),"src":"$PRAVDA_TMP_DIR/If.cs","ec":int32(17) + } + push int32(6) + push int32(2) + swapn + pop + jump @TestIfs_br43 + @TestIfs_br41: + meta source_mark { + "sl":int32(32),"sc":int32(8),"el":int32(32),"src":"$PRAVDA_TMP_DIR/If.cs","ec":int32(28) + } + push int32(8) + push int32(2) + swapn + pop + @TestIfs_br43: + push int32(1) + dupn + push int32(7) + swap + gt + push int8(3) + cast + push int32(0) + eq + not + jumpi @TestIfs_br52 + push int32(1) + dupn + push int32(10) + meta source_mark { + "sl":int32(33),"sc":int32(12),"el":int32(33),"src":"$PRAVDA_TMP_DIR/If.cs","ec":int32(18) + } + swap + gt + push int8(3) + cast + meta source_mark { + "sl":int32(34),"sc":int32(8),"el":int32(34),"src":"$PRAVDA_TMP_DIR/If.cs","ec":int32(9) + } + push int8(9) + cast + not + push int8(3) + cast + push int32(0) + eq + not + jumpi @TestIfs_br56 + @TestIfs_br52: + push int32(1) + meta source_mark { + "sl":int32(35),"sc":int32(12),"el":int32(35),"src":"$PRAVDA_TMP_DIR/If.cs","ec":int32(18) + } + push int32(2) + swapn + pop + jump @TestIfs_br58 + @TestIfs_br56: + push int32(0) + meta source_mark { + "sl":int32(38),"sc":int32(8),"el":int32(38),"src":"$PRAVDA_TMP_DIR/If.cs","ec":int32(39) + } + push int32(2) + swapn + pop + @TestIfs_br58: + push int32(1) + dupn + push int32(1) + swap + gt + push int8(3) + cast + push int8(9) + cast + not + push int8(3) + cast + push int32(0) + eq + not + jumpi @TestIfs_br66 + push int32(1) + dupn + push int32(3) + swap + lt + push int8(3) + cast + push int32(0) + eq + not + jumpi @TestIfs_br71 + @TestIfs_br66: + push int32(1) + dupn + push int32(20) + meta source_mark { + "sl":int32(39),"sc":int32(12),"el":int32(39),"src":"$PRAVDA_TMP_DIR/If.cs","ec":int32(18) + } + swap + gt + push int8(3) + cast + meta source_mark { + "sl":int32(40),"sc":int32(8),"el":int32(40),"src":"$PRAVDA_TMP_DIR/If.cs","ec":int32(9) + } + push int8(9) + cast + not + push int8(3) + cast + push int32(0) + eq + not + jumpi @TestIfs_br74 + @TestIfs_br71: + meta source_mark { + "sl":int32(41),"sc":int32(12),"el":int32(41),"src":"$PRAVDA_TMP_DIR/If.cs","ec":int32(18) + } + push int32(2) + push int32(2) + swapn + pop + meta source_mark { + "sl":int32(43),"sc":int32(4),"el":int32(43),"src":"$PRAVDA_TMP_DIR/If.cs","ec":int32(5) + } + jump @TestIfs_lvc + @TestIfs_br74: + push int32(3) + push int32(2) + swapn + pop + jump @TestIfs_lvc + @TestIfs_lvc: + pop + pop + jump @stop + @method_ctor: + meta method { + "name":"ctor","returnTpe":int8(0) + } + push "init" + sexist + not + jumpi @ctor_ok + push "Program has been already initialized" + throw + @ctor_ok: + push null + push "init" + sput + jump @ctor_lvc + @ctor_lvc: + pop + jump @stop + @stop: diff --git a/dotnet/src/test/resources/translation/Inheritance.trs b/dotnet/src/test/resources/translation/Inheritance.trs new file mode 100644 index 00000000..ff0aa94d --- /dev/null +++ b/dotnet/src/test/resources/translation/Inheritance.trs @@ -0,0 +1,276 @@ +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: Inheritance.exe + sources: + - Pravda.dll + - dotnet-tests/resources/Inheritance.cs + optimize: true +--- +translation: |- + meta custom "CIL" + meta program_name "Inheritance" + dup + push "ctor" + eq + jumpi @method_ctor + push "init" + sexist + jumpi @methods + push "Program was not initialized" + throw + @methods: + dup + push "TestInheritance" + eq + jumpi @method_TestInheritance + push "Wrong method name" + throw + @method_TestInheritance: + meta method { + "name":"TestInheritance","returnTpe":int8(3) + } + push null + push null + meta source_mark { + "sl":int32(53),"sc":int32(9),"el":int32(53),"src":"$PRAVDA_TMP_DIR/Inheritance.cs","ec":int32(31) + } + push int32(100) + new {} + call @vtable_A + call @default_fields_A + push int32(2) + swapn + call @func_A.ctor_int32 + meta source_mark { + "sl":int32(54),"sc":int32(9),"el":int32(54),"src":"$PRAVDA_TMP_DIR/Inheritance.cs","ec":int32(31) + } + push int32(200) + new {} + call @vtable_B + call @default_fields_B + push int32(2) + swapn + call @func_B.ctor_int32 + push int32(4) + swapn + pop + meta source_mark { + "sl":int32(55),"sc":int32(9),"el":int32(55),"src":"$PRAVDA_TMP_DIR/Inheritance.cs","ec":int32(41) + } + dup + push int32(1) + dupn + struct_get "Answer" + call + swap + pop + pop + push int32(3) + dupn + push int32(1) + dupn + struct_get "Answer" + call + swap + pop + pop + meta source_mark { + "sl":int32(56),"sc":int32(9),"el":int32(56),"src":"$PRAVDA_TMP_DIR/Inheritance.cs","ec":int32(33) + } + push int32(1) + dupn + struct_get "AnswerPlus1" + call + swap + pop + meta source_mark { + "sl":int32(57),"sc":int32(9),"el":int32(57),"src":"$PRAVDA_TMP_DIR/Inheritance.cs","ec":int32(33) + } + push int32(3) + dupn + push int32(1) + dupn + struct_get "AnswerPlus1" + call + swap + pop + push int32(3) + swapn + pop + meta source_mark { + "sl":int32(58),"sc":int32(9),"el":int32(58),"src":"$PRAVDA_TMP_DIR/Inheritance.cs","ec":int32(22) + } + push int32(2) + dupn + add + jump @TestInheritance_lvc + @TestInheritance_lvc: + swap + pop + swap + pop + swap + pop + jump @stop + @method_ctor: + meta method { + "name":"ctor","returnTpe":int8(0) + } + push "init" + sexist + not + jumpi @ctor_ok + push "Program has been already initialized" + throw + @ctor_ok: + push null + push "init" + sput + jump @ctor_lvc + @ctor_lvc: + pop + jump @stop + @default_fields_A: + dup + push int32(0) + struct_mut "AVal" + ret + @default_fields_B: + dup + push int32(0) + struct_mut "BVal" + ret + @func_A.Answer: + meta source_mark { + "sl":int32(29),"sc":int32(9),"el":int32(29),"src":"$PRAVDA_TMP_DIR/Inheritance.cs","ec":int32(26) + } + push int32(1) + dupn + struct_get "AVal" + push int32(42) + add + jump @A.Answer_lvc + @A.Answer_lvc: + ret + @func_A.ctor_int32: + meta source_mark { + "sl":int32(22),"sc":int32(25),"el":int32(22),"src":"$PRAVDA_TMP_DIR/Inheritance.cs","ec":int32(35) + } + push int32(2) + dupn + push int32(2) + dupn + call @func_Parent.ctor_int32 + pop + meta source_mark { + "sl":int32(24),"sc":int32(9),"el":int32(24),"src":"$PRAVDA_TMP_DIR/Inheritance.cs","ec":int32(26) + } + push int32(2) + dupn + push int32(2) + dupn + struct_mut "AVal" + meta source_mark { + "sl":int32(25),"sc":int32(5),"el":int32(25),"src":"$PRAVDA_TMP_DIR/Inheritance.cs","ec":int32(6) + } + jump @A.ctor_int32_lvc + @A.ctor_int32_lvc: + pop + ret + @func_B.Answer: + meta source_mark { + "sl":int32(44),"sc":int32(9),"el":int32(44),"src":"$PRAVDA_TMP_DIR/Inheritance.cs","ec":int32(26) + } + push int32(1) + dupn + struct_get "BVal" + push int32(43) + add + jump @B.Answer_lvc + @B.Answer_lvc: + ret + @func_B.ctor_int32: + meta source_mark { + "sl":int32(37),"sc":int32(25),"el":int32(37),"src":"$PRAVDA_TMP_DIR/Inheritance.cs","ec":int32(35) + } + push int32(2) + dupn + push int32(2) + dupn + call @func_Parent.ctor_int32 + pop + meta source_mark { + "sl":int32(39),"sc":int32(9),"el":int32(39),"src":"$PRAVDA_TMP_DIR/Inheritance.cs","ec":int32(26) + } + push int32(2) + dupn + push int32(2) + dupn + struct_mut "BVal" + meta source_mark { + "sl":int32(40),"sc":int32(5),"el":int32(40),"src":"$PRAVDA_TMP_DIR/Inheritance.cs","ec":int32(6) + } + jump @B.ctor_int32_lvc + @B.ctor_int32_lvc: + pop + ret + @func_Parent.Answer: + meta source_mark { + "sl":int32(14),"sc":int32(9),"el":int32(14),"src":"$PRAVDA_TMP_DIR/Inheritance.cs","ec":int32(18) + } + push int32(0) + jump @Parent.Answer_lvc + @Parent.Answer_lvc: + ret + @func_Parent.AnswerPlus1: + meta source_mark { + "sl":int32(9),"sc":int32(9),"el":int32(9),"src":"$PRAVDA_TMP_DIR/Inheritance.cs","ec":int32(29) + } + push int32(1) + dupn + push int32(1) + dupn + struct_get "Answer" + call + swap + pop + push int32(1) + add + jump @Parent.AnswerPlus1_lvc + @Parent.AnswerPlus1_lvc: + ret + @func_Parent.ctor_int32: + meta source_mark { + "sl":int32(5),"sc":int32(5),"el":int32(5),"src":"$PRAVDA_TMP_DIR/Inheritance.cs","ec":int32(27) + } + push int32(2) + dupn + pop + meta source_mark { + "sl":int32(5),"sc":int32(29),"el":int32(5),"src":"$PRAVDA_TMP_DIR/Inheritance.cs","ec":int32(30) + } + jump @Parent.ctor_int32_lvc + @Parent.ctor_int32_lvc: + pop + ret + @vtable_A: + dup + push @func_A.Answer + struct_mut "Answer" + dup + push @func_Parent.AnswerPlus1 + struct_mut "AnswerPlus1" + ret + @vtable_B: + dup + push @func_B.Answer + struct_mut "Answer" + dup + push @func_Parent.AnswerPlus1 + struct_mut "AnswerPlus1" + ret + @stop: diff --git a/dotnet/src/test/resources/translation/IntBuffer.trs b/dotnet/src/test/resources/translation/IntBuffer.trs new file mode 100644 index 00000000..50bbd96e --- /dev/null +++ b/dotnet/src/test/resources/translation/IntBuffer.trs @@ -0,0 +1,450 @@ +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: IntBuffer.exe + sources: + - Pravda.dll + - dotnet-tests/resources/IntBuffer.cs + optimize: true +--- +translation: |- + meta custom "CIL" + meta program_name "IntBufferProgram" + dup + push "ctor" + eq + jumpi @method_ctor + push "init" + sexist + jumpi @methods + push "Program was not initialized" + throw + @methods: + dup + push "TestBuffer" + eq + jumpi @method_TestBuffer + push "Wrong method name" + throw + @method_TestBuffer: + meta method { + "name":"TestBuffer","returnTpe":int8(11) + } + push null + push null + push null + push null + meta source_mark { + "sl":int32(42),"sc":int32(9),"el":int32(42),"src":"$PRAVDA_TMP_DIR/IntBuffer.cs","ec":int32(43) + } + push int32(2) + new {} + call @vtable_IntBuffer + call @default_fields_IntBuffer + push int32(2) + swapn + call @func_IntBuffer.ctor_int32 + meta source_mark { + "sl":int32(43),"sc":int32(9),"el":int32(43),"src":"$PRAVDA_TMP_DIR/IntBuffer.cs","ec":int32(24) + } + dup + push int32(1) + call @func_IntBuffer.Append_int32 + pop + meta source_mark { + "sl":int32(44),"sc":int32(9),"el":int32(44),"src":"$PRAVDA_TMP_DIR/IntBuffer.cs","ec":int32(24) + } + dup + push int32(3) + call @func_IntBuffer.Append_int32 + pop + meta source_mark { + "sl":int32(45),"sc":int32(9),"el":int32(45),"src":"$PRAVDA_TMP_DIR/IntBuffer.cs","ec":int32(24) + } + dup + push int32(5) + call @func_IntBuffer.Append_int32 + pop + meta source_mark { + "sl":int32(46),"sc":int32(9),"el":int32(46),"src":"$PRAVDA_TMP_DIR/IntBuffer.cs","ec":int32(24) + } + dup + push int32(7) + call @func_IntBuffer.Append_int32 + pop + meta source_mark { + "sl":int32(47),"sc":int32(9),"el":int32(47),"src":"$PRAVDA_TMP_DIR/IntBuffer.cs","ec":int32(24) + } + dup + push int32(9) + call @func_IntBuffer.Append_int32 + pop + meta source_mark { + "sl":int32(48),"sc":int32(9),"el":int32(48),"src":"$PRAVDA_TMP_DIR/IntBuffer.cs","ec":int32(25) + } + dup + push int32(11) + call @func_IntBuffer.Append_int32 + pop + meta source_mark { + "sl":int32(49),"sc":int32(9),"el":int32(49),"src":"$PRAVDA_TMP_DIR/IntBuffer.cs","ec":int32(25) + } + dup + push int32(13) + call @func_IntBuffer.Append_int32 + pop + meta source_mark { + "sl":int32(50),"sc":int32(9),"el":int32(50),"src":"$PRAVDA_TMP_DIR/IntBuffer.cs","ec":int32(25) + } + dup + push int32(15) + call @func_IntBuffer.Append_int32 + pop + meta source_mark { + "sl":int32(51),"sc":int32(9),"el":int32(51),"src":"$PRAVDA_TMP_DIR/IntBuffer.cs","ec":int32(25) + } + dup + push int32(17) + call @func_IntBuffer.Append_int32 + pop + meta source_mark { + "sl":int32(52),"sc":int32(9),"el":int32(52),"src":"$PRAVDA_TMP_DIR/IntBuffer.cs","ec":int32(25) + } + dup + push int32(0) + call @func_IntBuffer.get_Item_int32 + swap + pop + push int32(6) + swapn + pop + meta source_mark { + "sl":int32(53),"sc":int32(9),"el":int32(53),"src":"$PRAVDA_TMP_DIR/IntBuffer.cs","ec":int32(25) + } + dup + push int32(1) + call @func_IntBuffer.get_Item_int32 + swap + pop + push int32(5) + swapn + pop + meta source_mark { + "sl":int32(54),"sc":int32(9),"el":int32(54),"src":"$PRAVDA_TMP_DIR/IntBuffer.cs","ec":int32(25) + } + dup + push int32(2) + call @func_IntBuffer.get_Item_int32 + swap + pop + push int32(4) + swapn + pop + meta source_mark { + "sl":int32(55),"sc":int32(9),"el":int32(55),"src":"$PRAVDA_TMP_DIR/IntBuffer.cs","ec":int32(22) + } + dup + push int32(1) + push int32(10) + call @func_IntBuffer.set_Item_int32_int32 + pop + meta source_mark { + "sl":int32(56),"sc":int32(9),"el":int32(56),"src":"$PRAVDA_TMP_DIR/IntBuffer.cs","ec":int32(25) + } + push int32(1) + call @func_IntBuffer.get_Item_int32 + swap + pop + push int32(2) + swapn + pop + meta source_mark { + "sl":int32(57),"sc":int32(9),"el":int32(57),"src":"$PRAVDA_TMP_DIR/IntBuffer.cs","ec":int32(102) + } + push int32(4) + dupn + push int8(11) + cast + push int32(4) + dupn + push int8(11) + cast + push int32(4) + dupn + push int8(11) + cast + push int32(4) + dupn + push int8(11) + cast + swap + concat + swap + concat + swap + concat + jump @TestBuffer_lvc + @TestBuffer_lvc: + swap + pop + swap + pop + swap + pop + swap + pop + swap + pop + jump @stop + @method_ctor: + meta method { + "name":"ctor","returnTpe":int8(0) + } + push "init" + sexist + not + jumpi @ctor_ok + push "Program has been already initialized" + throw + @ctor_ok: + push null + push "init" + sput + jump @ctor_lvc + @ctor_lvc: + pop + jump @stop + @default_fields_IntBuffer: + dup + push int32(0) + struct_mut "size" + dup + push null + struct_mut "buffer" + ret + @func_IntBuffer.Append_int32: + push null + push null + meta source_mark { + "sl":int32(21),"sc":int32(9),"el":int32(21),"src":"$PRAVDA_TMP_DIR/IntBuffer.cs","ec":int32(35) + } + push int32(4) + dupn + struct_get "size" + push int32(5) + dupn + struct_get "buffer" + length + push int8(3) + cast + eq + push int8(3) + cast + meta source_mark { + "sl":int32(23),"sc":int32(13),"el":int32(23),"src":"$PRAVDA_TMP_DIR/IntBuffer.cs","ec":int32(62) + } + push int8(9) + cast + not + push int8(3) + cast + push int32(0) + eq + not + jumpi @IntBuffer.Append_int32_br71 + push int32(4) + dupn + struct_get "buffer" + length + push int8(3) + cast + push int32(2) + mul + push int32(1) + add + push int8(3) + new_array + meta source_mark { + "sl":int32(24),"sc":int32(18),"el":int32(24),"src":"$PRAVDA_TMP_DIR/IntBuffer.cs","ec":int32(27) + } + push int32(3) + swapn + pop + push int32(0) + push int32(2) + swapn + pop + jump @IntBuffer.Append_int32_br53 + @IntBuffer.Append_int32_br38: + push int32(2) + dupn + meta source_mark { + "sl":int32(25),"sc":int32(17),"el":int32(25),"src":"$PRAVDA_TMP_DIR/IntBuffer.cs","ec":int32(42) + } + push int32(2) + dupn + push int32(6) + dupn + struct_get "buffer" + push int32(4) + dupn + array_get + swap + array_mut + push int32(1) + dupn + meta source_mark { + "sl":int32(24),"sc":int32(48),"el":int32(24),"src":"$PRAVDA_TMP_DIR/IntBuffer.cs","ec":int32(51) + } + push int32(1) + add + push int32(2) + swapn + pop + @IntBuffer.Append_int32_br53: + push int32(1) + dupn + meta source_mark { + "sl":int32(24),"sc":int32(29),"el":int32(24),"src":"$PRAVDA_TMP_DIR/IntBuffer.cs","ec":int32(46) + } + push int32(5) + dupn + struct_get "buffer" + length + push int8(3) + cast + swap + lt + push int8(3) + cast + push int32(0) + eq + not + jumpi @IntBuffer.Append_int32_br38 + push int32(4) + dupn + meta source_mark { + "sl":int32(27),"sc":int32(13),"el":int32(27),"src":"$PRAVDA_TMP_DIR/IntBuffer.cs","ec":int32(32) + } + push int32(3) + dupn + struct_mut "buffer" + @IntBuffer.Append_int32_br71: + push int32(4) + dupn + meta source_mark { + "sl":int32(30),"sc":int32(9),"el":int32(30),"src":"$PRAVDA_TMP_DIR/IntBuffer.cs","ec":int32(29) + } + struct_get "buffer" + push int32(5) + dupn + struct_get "size" + push int32(5) + dupn + swap + array_mut + push int32(4) + dupn + meta source_mark { + "sl":int32(31),"sc":int32(9),"el":int32(31),"src":"$PRAVDA_TMP_DIR/IntBuffer.cs","ec":int32(19) + } + push int32(5) + dupn + struct_get "size" + push int32(1) + add + struct_mut "size" + jump @IntBuffer.Append_int32_lvc + @IntBuffer.Append_int32_lvc: + pop + pop + pop + ret + @func_IntBuffer.ctor: + meta source_mark { + "sl":int32(9),"sc":int32(5),"el":int32(9),"src":"$PRAVDA_TMP_DIR/IntBuffer.cs","ec":int32(23) + } + push int32(1) + dupn + pop + meta source_mark { + "sl":int32(11),"sc":int32(9),"el":int32(11),"src":"$PRAVDA_TMP_DIR/IntBuffer.cs","ec":int32(30) + } + push int32(1) + dupn + push int32(16) + push int8(3) + new_array + struct_mut "buffer" + meta source_mark { + "sl":int32(12),"sc":int32(5),"el":int32(12),"src":"$PRAVDA_TMP_DIR/IntBuffer.cs","ec":int32(6) + } + jump @IntBuffer.ctor_lvc + @IntBuffer.ctor_lvc: + ret + @func_IntBuffer.ctor_int32: + meta source_mark { + "sl":int32(14),"sc":int32(5),"el":int32(14),"src":"$PRAVDA_TMP_DIR/IntBuffer.cs","ec":int32(35) + } + push int32(2) + dupn + pop + meta source_mark { + "sl":int32(16),"sc":int32(9),"el":int32(16),"src":"$PRAVDA_TMP_DIR/IntBuffer.cs","ec":int32(36) + } + push int32(2) + dupn + push int32(2) + dupn + push int8(3) + new_array + struct_mut "buffer" + meta source_mark { + "sl":int32(17),"sc":int32(5),"el":int32(17),"src":"$PRAVDA_TMP_DIR/IntBuffer.cs","ec":int32(6) + } + jump @IntBuffer.ctor_int32_lvc + @IntBuffer.ctor_int32_lvc: + pop + ret + @func_IntBuffer.get_Item_int32: + meta source_mark { + "sl":int32(34),"sc":int32(36),"el":int32(34),"src":"$PRAVDA_TMP_DIR/IntBuffer.cs","ec":int32(53) + } + push int32(2) + dupn + struct_get "buffer" + push int32(2) + dupn + array_get + jump @IntBuffer.get_Item_int32_lvc + @IntBuffer.get_Item_int32_lvc: + swap + pop + ret + @func_IntBuffer.set_Item_int32_int32: + meta source_mark { + "sl":int32(34),"sc":int32(62),"el":int32(34),"src":"$PRAVDA_TMP_DIR/IntBuffer.cs","ec":int32(80) + } + push int32(3) + dupn + struct_get "buffer" + push int32(3) + dupn + push int32(3) + dupn + swap + array_mut + meta source_mark { + "sl":int32(34),"sc":int32(81),"el":int32(34),"src":"$PRAVDA_TMP_DIR/IntBuffer.cs","ec":int32(82) + } + jump @IntBuffer.set_Item_int32_int32_lvc + @IntBuffer.set_Item_int32_int32_lvc: + pop + pop + ret + @vtable_IntBuffer: + ret + @stop: diff --git a/dotnet/src/test/resources/translation/Loop.trs b/dotnet/src/test/resources/translation/Loop.trs new file mode 100644 index 00000000..cf6a7e24 --- /dev/null +++ b/dotnet/src/test/resources/translation/Loop.trs @@ -0,0 +1,145 @@ +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: Loop.exe + sources: + - Pravda.dll + - dotnet-tests/resources/Loop.cs + optimize: true +--- +translation: |- + meta custom "CIL" + meta program_name "Loop" + dup + push "ctor" + eq + jumpi @method_ctor + push "init" + sexist + jumpi @methods + push "Program was not initialized" + throw + @methods: + dup + push "TestLoop" + eq + jumpi @method_TestLoop + push "Wrong method name" + throw + @method_TestLoop: + meta method { + "name":"TestLoop","returnTpe":int8(3) + } + push null + push null + meta source_mark { + "sl":int32(8),"sc":int32(8),"el":int32(8),"src":"$PRAVDA_TMP_DIR/Loop.cs","ec":int32(18) + } + push int32(0) + push int32(3) + swapn + pop + meta source_mark { + "sl":int32(9),"sc":int32(13),"el":int32(9),"src":"$PRAVDA_TMP_DIR/Loop.cs","ec":int32(22) + } + push int32(0) + push int32(2) + swapn + pop + jump @TestLoop_br14 + @TestLoop_br6: + push int32(2) + dupn + push int32(2) + meta source_mark { + "sl":int32(10),"sc":int32(12),"el":int32(10),"src":"$PRAVDA_TMP_DIR/Loop.cs","ec":int32(19) + } + add + push int32(3) + swapn + pop + push int32(1) + dupn + push int32(1) + meta source_mark { + "sl":int32(9),"sc":int32(32),"el":int32(9),"src":"$PRAVDA_TMP_DIR/Loop.cs","ec":int32(35) + } + add + push int32(2) + swapn + pop + @TestLoop_br14: + push int32(1) + dupn + meta source_mark { + "sl":int32(9),"sc":int32(24),"el":int32(9),"src":"$PRAVDA_TMP_DIR/Loop.cs","ec":int32(30) + } + push int32(10) + swap + lt + push int8(3) + cast + push int32(0) + eq + not + jumpi @TestLoop_br6 + jump @TestLoop_br25 + @TestLoop_br21: + push int32(2) + dupn + push int32(2) + mul + push int32(3) + swapn + pop + @TestLoop_br25: + meta source_mark { + "sl":int32(14),"sc":int32(11),"el":int32(14),"src":"$PRAVDA_TMP_DIR/Loop.cs","ec":int32(18) + } + push int32(2) + dupn + meta source_mark { + "sl":int32(13),"sc":int32(8),"el":int32(13),"src":"$PRAVDA_TMP_DIR/Loop.cs","ec":int32(25) + } + push int32(10000) + swap + lt + push int8(3) + cast + push int32(0) + eq + not + jumpi @TestLoop_br21 + push int32(2) + dupn + jump @TestLoop_lvc + @TestLoop_lvc: + swap + pop + swap + pop + swap + pop + jump @stop + @method_ctor: + meta method { + "name":"ctor","returnTpe":int8(0) + } + push "init" + sexist + not + jumpi @ctor_ok + push "Program has been already initialized" + throw + @ctor_ok: + push null + push "init" + sput + jump @ctor_lvc + @ctor_lvc: + pop + jump @stop + @stop: diff --git a/dotnet/src/test/resources/translation/LoopNested.trs b/dotnet/src/test/resources/translation/LoopNested.trs new file mode 100644 index 00000000..bc7f9bcf --- /dev/null +++ b/dotnet/src/test/resources/translation/LoopNested.trs @@ -0,0 +1,202 @@ +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: LoopNested.exe + sources: + - Pravda.dll + - dotnet-tests/resources/LoopNested.cs + optimize: true +--- +translation: |- + meta custom "CIL" + meta program_name "LoosNested" + dup + push "ctor" + eq + jumpi @method_ctor + push "init" + sexist + jumpi @methods + push "Program was not initialized" + throw + @methods: + dup + push "TestNestedLoop" + eq + jumpi @method_TestNestedLoop + push "Wrong method name" + throw + @method_TestNestedLoop: + meta method { + "name":"TestNestedLoop","returnTpe":int8(3) + } + push null + push null + push null + meta source_mark { + "sl":int32(8),"sc":int32(9),"el":int32(8),"src":"$PRAVDA_TMP_DIR/LoopNested.cs","ec":int32(19) + } + push int32(0) + push int32(4) + swapn + pop + meta source_mark { + "sl":int32(9),"sc":int32(14),"el":int32(9),"src":"$PRAVDA_TMP_DIR/LoopNested.cs","ec":int32(23) + } + push int32(0) + push int32(3) + swapn + pop + jump @TestNestedLoop_br40 + @TestNestedLoop_br6: + push int32(0) + push int32(2) + swapn + pop + jump @TestNestedLoop_br31 + @TestNestedLoop_br10: + meta source_mark { + "sl":int32(10),"sc":int32(18),"el":int32(10),"src":"$PRAVDA_TMP_DIR/LoopNested.cs","ec":int32(27) + } + push int32(3) + dupn + push int32(2) + swap + mod + push int32(0) + eq + not + jumpi @TestNestedLoop_br27 + push int32(3) + dupn + meta source_mark { + "sl":int32(11),"sc":int32(14),"el":int32(11),"src":"$PRAVDA_TMP_DIR/LoopNested.cs","ec":int32(29) + } + push int32(3) + dupn + push int32(3) + dupn + add + meta source_mark { + "sl":int32(12),"sc":int32(21),"el":int32(12),"src":"$PRAVDA_TMP_DIR/LoopNested.cs","ec":int32(47) + } + push int32(1000000007) + swap + mod + add + push int32(4) + swapn + pop + @TestNestedLoop_br27: + push int32(1) + dupn + push int32(1) + add + push int32(2) + swapn + pop + @TestNestedLoop_br31: + push int32(1) + dupn + meta source_mark { + "sl":int32(10),"sc":int32(37),"el":int32(10),"src":"$PRAVDA_TMP_DIR/LoopNested.cs","ec":int32(40) + } + push int32(20) + swap + lt + push int8(3) + cast + push int32(0) + eq + not + jumpi @TestNestedLoop_br10 + push int32(2) + dupn + meta source_mark { + "sl":int32(10),"sc":int32(29),"el":int32(10),"src":"$PRAVDA_TMP_DIR/LoopNested.cs","ec":int32(35) + } + push int32(1) + add + push int32(3) + swapn + pop + @TestNestedLoop_br40: + push int32(2) + dupn + meta source_mark { + "sl":int32(9),"sc":int32(33),"el":int32(9),"src":"$PRAVDA_TMP_DIR/LoopNested.cs","ec":int32(36) + } + push int32(10) + swap + lt + push int8(3) + cast + push int32(0) + eq + not + jumpi @TestNestedLoop_br6 + jump @TestNestedLoop_br51 + @TestNestedLoop_br47: + push int32(3) + dupn + meta source_mark { + "sl":int32(9),"sc":int32(25),"el":int32(9),"src":"$PRAVDA_TMP_DIR/LoopNested.cs","ec":int32(31) + } + push int32(2) + mul + push int32(4) + swapn + pop + @TestNestedLoop_br51: + push int32(3) + dupn + meta source_mark { + "sl":int32(18),"sc":int32(13),"el":int32(18),"src":"$PRAVDA_TMP_DIR/LoopNested.cs","ec":int32(20) + } + push int32(10000) + swap + lt + push int8(3) + cast + push int32(0) + eq + not + jumpi @TestNestedLoop_br47 + meta source_mark { + "sl":int32(17),"sc":int32(9),"el":int32(17),"src":"$PRAVDA_TMP_DIR/LoopNested.cs","ec":int32(26) + } + push int32(3) + dupn + jump @TestNestedLoop_lvc + @TestNestedLoop_lvc: + swap + pop + swap + pop + swap + pop + swap + pop + jump @stop + @method_ctor: + meta method { + "name":"ctor","returnTpe":int8(0) + } + push "init" + sexist + not + jumpi @ctor_ok + push "Program has been already initialized" + throw + @ctor_ok: + push null + push "init" + sput + jump @ctor_lvc + @ctor_lvc: + pop + jump @stop + @stop: diff --git a/dotnet/src/test/resources/translation/Object.trs b/dotnet/src/test/resources/translation/Object.trs new file mode 100644 index 00000000..ef9b7f44 --- /dev/null +++ b/dotnet/src/test/resources/translation/Object.trs @@ -0,0 +1,182 @@ +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: Object.exe + sources: + - Pravda.dll + - dotnet-tests/resources/Object.cs + optimize: true +--- +translation: |- + meta custom "CIL" + meta program_name "Object" + dup + push "ctor" + eq + jumpi @method_ctor + push "init" + sexist + jumpi @methods + push "Program was not initialized" + throw + @methods: + dup + push "TestObjects" + eq + jumpi @method_TestObjects + push "Wrong method name" + throw + @method_TestObjects: + meta method { + "name":"TestObjects","returnTpe":int8(3) + } + push null + meta source_mark { + "sl":int32(39),"sc":int32(9),"el":int32(39),"src":"$PRAVDA_TMP_DIR/Object.cs","ec":int32(28) + } + push int32(100) + new {} + call @vtable_A + call @default_fields_A + push int32(2) + swapn + call @func_A.ctor_int32 + meta source_mark { + "sl":int32(40),"sc":int32(9),"el":int32(40),"src":"$PRAVDA_TMP_DIR/Object.cs","ec":int32(28) + } + push int32(200) + new {} + call @vtable_B + call @default_fields_B + push int32(2) + swapn + call @func_B.ctor_int32 + push int32(3) + swapn + pop + meta source_mark { + "sl":int32(41),"sc":int32(9),"el":int32(41),"src":"$PRAVDA_TMP_DIR/Object.cs","ec":int32(43) + } + call @func_A.AnswerA + swap + pop + push int32(2) + dupn + call @func_B.AnswerB + swap + pop + add + meta source_mark { + "sl":int32(42),"sc":int32(9),"el":int32(42),"src":"$PRAVDA_TMP_DIR/Object.cs","ec":int32(18) + } + jump @TestObjects_lvc + @TestObjects_lvc: + swap + pop + swap + pop + jump @stop + @method_ctor: + meta method { + "name":"ctor","returnTpe":int8(0) + } + push "init" + sexist + not + jumpi @ctor_ok + push "Program has been already initialized" + throw + @ctor_ok: + push null + push "init" + sput + jump @ctor_lvc + @ctor_lvc: + pop + jump @stop + @default_fields_A: + dup + push int32(0) + struct_mut "AVal" + ret + @default_fields_B: + dup + push int32(0) + struct_mut "BVal" + ret + @func_A.AnswerA: + meta source_mark { + "sl":int32(15),"sc":int32(8),"el":int32(15),"src":"$PRAVDA_TMP_DIR/Object.cs","ec":int32(25) + } + push int32(1) + dupn + struct_get "AVal" + push int32(42) + add + jump @A.AnswerA_lvc + @A.AnswerA_lvc: + ret + @func_A.ctor_int32: + meta source_mark { + "sl":int32(8),"sc":int32(4),"el":int32(8),"src":"$PRAVDA_TMP_DIR/Object.cs","ec":int32(22) + } + push int32(2) + dupn + pop + meta source_mark { + "sl":int32(10),"sc":int32(8),"el":int32(10),"src":"$PRAVDA_TMP_DIR/Object.cs","ec":int32(25) + } + push int32(2) + dupn + push int32(2) + dupn + struct_mut "AVal" + meta source_mark { + "sl":int32(11),"sc":int32(4),"el":int32(11),"src":"$PRAVDA_TMP_DIR/Object.cs","ec":int32(5) + } + jump @A.ctor_int32_lvc + @A.ctor_int32_lvc: + pop + ret + @func_B.AnswerB: + meta source_mark { + "sl":int32(30),"sc":int32(9),"el":int32(30),"src":"$PRAVDA_TMP_DIR/Object.cs","ec":int32(26) + } + push int32(1) + dupn + struct_get "BVal" + push int32(43) + add + jump @B.AnswerB_lvc + @B.AnswerB_lvc: + ret + @func_B.ctor_int32: + meta source_mark { + "sl":int32(23),"sc":int32(5),"el":int32(23),"src":"$PRAVDA_TMP_DIR/Object.cs","ec":int32(23) + } + push int32(2) + dupn + pop + meta source_mark { + "sl":int32(25),"sc":int32(9),"el":int32(25),"src":"$PRAVDA_TMP_DIR/Object.cs","ec":int32(26) + } + push int32(2) + dupn + push int32(2) + dupn + struct_mut "BVal" + meta source_mark { + "sl":int32(26),"sc":int32(5),"el":int32(26),"src":"$PRAVDA_TMP_DIR/Object.cs","ec":int32(6) + } + jump @B.ctor_int32_lvc + @B.ctor_int32_lvc: + pop + ret + @vtable_A: + ret + @vtable_B: + ret + @stop: diff --git a/dotnet/src/test/resources/translation/ObjectGetSet.trs b/dotnet/src/test/resources/translation/ObjectGetSet.trs new file mode 100644 index 00000000..6baa529c --- /dev/null +++ b/dotnet/src/test/resources/translation/ObjectGetSet.trs @@ -0,0 +1,182 @@ +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: ObjectGetSet.exe + sources: + - Pravda.dll + - dotnet-tests/resources/ObjectGetSet.cs + optimize: true +--- +translation: |- + meta custom "CIL" + meta program_name "ObjectGetSet" + dup + push "ctor" + eq + jumpi @method_ctor + push "init" + sexist + jumpi @methods + push "Program was not initialized" + throw + @methods: + dup + push "TestObjectGetSet" + eq + jumpi @method_TestObjectGetSet + push "Wrong method name" + throw + @method_TestObjectGetSet: + meta method { + "name":"TestObjectGetSet","returnTpe":int8(3) + } + push null + meta source_mark { + "sl":int32(26),"sc":int32(9),"el":int32(26),"src":"$PRAVDA_TMP_DIR/ObjectGetSet.cs","ec":int32(35) + } + new {} + call @vtable_SomeClass + call @default_fields_SomeClass + call @func_SomeClass.ctor + push int32(2) + swapn + pop + meta source_mark { + "sl":int32(27),"sc":int32(9),"el":int32(27),"src":"$PRAVDA_TMP_DIR/ObjectGetSet.cs","ec":int32(24) + } + push int32(1) + dupn + push int32(3) + struct_mut "field1" + meta source_mark { + "sl":int32(28),"sc":int32(9),"el":int32(28),"src":"$PRAVDA_TMP_DIR/ObjectGetSet.cs","ec":int32(25) + } + push int32(1) + dupn + push int32(20) + call @func_SomeClass.set_field2_int32 + pop + meta source_mark { + "sl":int32(29),"sc":int32(9),"el":int32(29),"src":"$PRAVDA_TMP_DIR/ObjectGetSet.cs","ec":int32(28) + } + push int32(1) + dupn + push int32(100) + call @func_SomeClass.SetField3_int32 + pop + meta source_mark { + "sl":int32(31),"sc":int32(9),"el":int32(31),"src":"$PRAVDA_TMP_DIR/ObjectGetSet.cs","ec":int32(58) + } + push int32(1) + dupn + struct_get "field1" + push int32(2) + dupn + call @func_SomeClass.get_field2 + swap + pop + add + push int32(2) + dupn + call @func_SomeClass.GetField3 + swap + pop + add + jump @TestObjectGetSet_lvc + @TestObjectGetSet_lvc: + swap + pop + swap + pop + jump @stop + @method_ctor: + meta method { + "name":"ctor","returnTpe":int8(0) + } + push "init" + sexist + not + jumpi @ctor_ok + push "Program has been already initialized" + throw + @ctor_ok: + push null + push "init" + sput + jump @ctor_lvc + @ctor_lvc: + pop + jump @stop + @default_fields_SomeClass: + dup + push int32(0) + struct_mut "field1" + dup + push int32(0) + struct_mut "k__BackingField" + dup + push int32(0) + struct_mut "field3" + ret + @func_SomeClass.GetField3: + meta source_mark { + "sl":int32(17),"sc":int32(9),"el":int32(17),"src":"$PRAVDA_TMP_DIR/ObjectGetSet.cs","ec":int32(23) + } + push int32(1) + dupn + struct_get "field3" + jump @SomeClass.GetField3_lvc + @SomeClass.GetField3_lvc: + ret + @func_SomeClass.SetField3_int32: + meta source_mark { + "sl":int32(12),"sc":int32(9),"el":int32(12),"src":"$PRAVDA_TMP_DIR/ObjectGetSet.cs","ec":int32(30) + } + push int32(2) + dupn + push int32(2) + dupn + struct_mut "field3" + meta source_mark { + "sl":int32(13),"sc":int32(5),"el":int32(13),"src":"$PRAVDA_TMP_DIR/ObjectGetSet.cs","ec":int32(6) + } + jump @SomeClass.SetField3_int32_lvc + @SomeClass.SetField3_int32_lvc: + pop + ret + @func_SomeClass.ctor: + push int32(1) + dupn + pop + jump @SomeClass.ctor_lvc + @SomeClass.ctor_lvc: + ret + @func_SomeClass.get_field2: + meta source_mark { + "sl":int32(7),"sc":int32(25),"el":int32(7),"src":"$PRAVDA_TMP_DIR/ObjectGetSet.cs","ec":int32(29) + } + push int32(1) + dupn + struct_get "k__BackingField" + jump @SomeClass.get_field2_lvc + @SomeClass.get_field2_lvc: + ret + @func_SomeClass.set_field2_int32: + meta source_mark { + "sl":int32(7),"sc":int32(30),"el":int32(7),"src":"$PRAVDA_TMP_DIR/ObjectGetSet.cs","ec":int32(34) + } + push int32(2) + dupn + push int32(2) + dupn + struct_mut "k__BackingField" + jump @SomeClass.set_field2_int32_lvc + @SomeClass.set_field2_int32_lvc: + pop + ret + @vtable_SomeClass: + ret + @stop: diff --git a/dotnet/src/test/resources/translation/pcall.trs b/dotnet/src/test/resources/translation/Pcall.trs similarity index 56% rename from dotnet/src/test/resources/translation/pcall.trs rename to dotnet/src/test/resources/translation/Pcall.trs index 31a93934..51236c61 100644 --- a/dotnet/src/test/resources/translation/pcall.trs +++ b/dotnet/src/test/resources/translation/Pcall.trs @@ -1,9 +1,25 @@ -exe: |- - pcall.exe pcall_program.dll - Expload.Pravda.Programs.MyProgram +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: PcallProgram.dll + sources: + - Pravda.dll + - dotnet-tests/resources/PcallProgram.cs + optimize: true + - target: Pcall.exe + sources: + - Pravda.dll + - PcallProgram.dll + - dotnet-tests/resources/Pcall.cs + optimize: true + main-class: PcallNamespace.Pcall --- translation: |- meta custom "CIL" + meta program_name "PcallNamespacePcall" dup push "ctor" eq @@ -15,46 +31,26 @@ translation: |- throw @methods: dup - push "pcall" + push "TestPcall" eq - jumpi @method_pcall + jumpi @method_TestPcall push "Wrong method name" throw - @method_ctor: - meta method { - "name":"ctor","returnTpe":int8(0) - } - push "init" - sexist - not - jumpi @ctor_ok - push "Program has been already initialized" - throw - @ctor_ok: - push null - push "init" - sput - jump @ctor_lvc - @ctor_lvc: - pop - jump @stop - @method_pcall: + @method_TestPcall: meta method { - "name":"pcall","returnTpe":int8(3) + "name":"TestPcall","returnTpe":int8(3) } push null push null - push null - push null - push null + meta source_mark { + "sl":int32(11),"sc":int32(12),"el":int32(11),"src":"$PRAVDA_TMP_DIR/Pcall.cs","ec":int32(107) + } push "1eaed20b7ce2b336043e4b340b031f95bb1ce6d935ef733ae4df1b66e1e3d91f" push int32(4) scall - push int32(6) - swapn - pop - push int32(5) - dupn + meta source_mark { + "sl":int32(12),"sc":int32(12),"el":int32(12),"src":"$PRAVDA_TMP_DIR/Pcall.cs","ec":int32(76) + } push int32(1) push int32(2) push int32(2) @@ -65,16 +61,17 @@ translation: |- swap push int32(3) pcall - push int32(5) + push int32(3) swapn pop + meta source_mark { + "sl":int32(13),"sc":int32(12),"el":int32(13),"src":"$PRAVDA_TMP_DIR/Pcall.cs","ec":int32(197) + } new int8[30, -82, -46, 11, 124, -30, -77, 54, 4, 62, 75, 52, 11, 3, 31, -107, -69, 28, -26, -39, 53, -17, 115, 58, -28, -33, 27, 102, -31, -29, -39, 31] call @stdlib_array_to_bytes - push int32(4) - swapn - pop - push int32(3) - dupn + meta source_mark { + "sl":int32(14),"sc":int32(12),"el":int32(14),"src":"$PRAVDA_TMP_DIR/Pcall.cs","ec":int32(76) + } push int32(3) push int32(4) push int32(2) @@ -85,32 +82,42 @@ translation: |- swap push int32(3) pcall - push int32(3) + push int32(2) swapn pop - push int32(4) - dupn - push int32(3) + meta source_mark { + "sl":int32(15),"sc":int32(12),"el":int32(15),"src":"$PRAVDA_TMP_DIR/Pcall.cs","ec":int32(25) + } + push int32(2) dupn - add push int32(2) - swapn - pop - push int32(1) dupn - jump @pcall_lvc - @pcall_lvc: - swap - pop - swap - pop + add + jump @TestPcall_lvc + @TestPcall_lvc: swap pop swap pop swap pop - swap + jump @stop + @method_ctor: + meta method { + "name":"ctor","returnTpe":int8(0) + } + push "init" + sexist + not + jumpi @ctor_ok + push "Program has been already initialized" + throw + @ctor_ok: + push null + push "init" + sput + jump @ctor_lvc + @ctor_lvc: pop jump @stop @stdlib_array_to_bytes: diff --git a/dotnet/src/test/resources/translation/ProgramInterface.trs b/dotnet/src/test/resources/translation/ProgramInterface.trs new file mode 100644 index 00000000..9937419c --- /dev/null +++ b/dotnet/src/test/resources/translation/ProgramInterface.trs @@ -0,0 +1,85 @@ +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: ProgramInterface.dll + sources: + - Pravda.dll + - dotnet-tests/resources/ProgramInterface.cs + optimize: true + - target: ProgramInterfaceCheck.exe + sources: + - Pravda.dll + - ProgramInterface.dll + - dotnet-tests/resources/ProgramInterfaceCheck.cs + optimize: true + main-class: InterfaceNamespace.ProgramInterfaceCheck +--- +translation: |- + meta custom "CIL" + meta program_name "InterfaceNamespaceProgramInterfaceCheck" + dup + push "ctor" + eq + jumpi @method_ctor + push "init" + sexist + jumpi @methods + push "Program was not initialized" + throw + @methods: + dup + push "CheckInterface" + eq + jumpi @method_CheckInterface + push "Wrong method name" + throw + @method_CheckInterface: + meta method { + "name":"CheckInterface","returnTpe":int8(3) + } + meta source_mark { + "sl":int32(11),"sc":int32(12),"el":int32(11),"src":"$PRAVDA_TMP_DIR/ProgramInterfaceCheck.cs","ec":int32(106) + } + push "1eaed20b7ce2b336043e4b340b031f95bb1ce6d935ef733ae4df1b66e1e3d91f" + push int32(4) + scall + meta source_mark { + "sl":int32(12),"sc":int32(12),"el":int32(12),"src":"$PRAVDA_TMP_DIR/ProgramInterfaceCheck.cs","ec":int32(78) + } + push int32(1) + push int32(2) + push int32(2) + swapn + push int32(3) + swapn + push "Add" + swap + push int32(3) + pcall + jump @CheckInterface_lvc + @CheckInterface_lvc: + swap + pop + jump @stop + @method_ctor: + meta method { + "name":"ctor","returnTpe":int8(0) + } + push "init" + sexist + not + jumpi @ctor_ok + push "Program has been already initialized" + throw + @ctor_ok: + push null + push "init" + sput + jump @ctor_lvc + @ctor_lvc: + pop + jump @stop + @stop: diff --git a/dotnet/src/test/resources/translation/SmartProgram.trs b/dotnet/src/test/resources/translation/SmartProgram.trs new file mode 100644 index 00000000..8ad16b37 --- /dev/null +++ b/dotnet/src/test/resources/translation/SmartProgram.trs @@ -0,0 +1,258 @@ +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: SmartProgram.exe + sources: + - Pravda.dll + - dotnet-tests/resources/SmartProgram.cs + optimize: true +--- +translation: |- + meta custom "CIL" + meta program_name "SmartProgram" + dup + push "ctor" + eq + jumpi @method_ctor + push "init" + sexist + jumpi @methods + push "Program was not initialized" + throw + @methods: + dup + push "BalanceOf" + eq + jumpi @method_BalanceOf + dup + push "Emit" + eq + jumpi @method_Emit + dup + push "Transfer" + eq + jumpi @method_Transfer + push "Wrong method name" + throw + @method_BalanceOf: + meta method { + "name":"BalanceOf",int32(0):int8(14),"returnTpe":int8(3) + } + meta source_mark { + "sl":int32(11),"sc":int32(9),"el":int32(11),"src":"$PRAVDA_TMP_DIR/SmartProgram.cs","ec":int32(53) + } + push x42616C616E636573 + push int32(3) + dupn + push int32(0) + call @stdlib_storage_get_default + jump @BalanceOf_lvc + @BalanceOf_lvc: + swap + pop + swap + pop + jump @stop + @method_Emit: + meta method { + "name":"Emit",int32(1):int8(3),int32(0):int8(14),"returnTpe":int8(0) + } + meta source_mark { + "sl":int32(26),"sc":int32(9),"el":int32(26),"src":"$PRAVDA_TMP_DIR/SmartProgram.cs","ec":int32(24) + } + push int32(2) + dupn + push int32(0) + swap + gt + push int8(3) + cast + meta source_mark { + "sl":int32(27),"sc":int32(13),"el":int32(27),"src":"$PRAVDA_TMP_DIR/SmartProgram.cs","ec":int32(72) + } + push int8(9) + cast + not + push int8(3) + cast + push int32(0) + eq + not + jumpi @Emit_br31 + push x42616C616E636573 + push int32(4) + dupn + push x42616C616E636573 + push int32(6) + dupn + push int32(0) + call @stdlib_storage_get_default + push int32(5) + dupn + add + meta source_mark { + "sl":int32(29),"sc":int32(5),"el":int32(29),"src":"$PRAVDA_TMP_DIR/SmartProgram.cs","ec":int32(6) + } + push int32(2) + dupn + push int8(14) + cast + push int32(4) + dupn + concat + sput + pop + pop + @Emit_br31: + jump @Emit_lvc + @Emit_lvc: + pop + pop + pop + jump @stop + @method_Transfer: + meta method { + "name":"Transfer",int32(1):int8(3),int32(0):int8(14),"returnTpe":int8(0) + } + meta source_mark { + "sl":int32(16),"sc":int32(9),"el":int32(16),"src":"$PRAVDA_TMP_DIR/SmartProgram.cs","ec":int32(24) + } + push int32(2) + dupn + push int32(0) + swap + gt + push int8(3) + cast + meta source_mark { + "sl":int32(17),"sc":int32(13),"el":int32(17),"src":"$PRAVDA_TMP_DIR/SmartProgram.cs","ec":int32(67) + } + push int8(9) + cast + not + push int8(3) + cast + push int32(0) + eq + not + jumpi @Transfer_br86 + push x42616C616E636573 + from + push int32(0) + call @stdlib_storage_get_default + push int32(3) + dupn + meta source_mark { + "sl":int32(18),"sc":int32(17),"el":int32(18),"src":"$PRAVDA_TMP_DIR/SmartProgram.cs","ec":int32(92) + } + swap + lt + push int8(3) + cast + push int32(0) + eq + not + jumpi @Transfer_br86 + push x42616C616E636573 + from + push x42616C616E636573 + from + push int32(0) + call @stdlib_storage_get_default + push int32(5) + dupn + push int32(-1) + mul + add + meta source_mark { + "sl":int32(19),"sc":int32(17),"el":int32(19),"src":"$PRAVDA_TMP_DIR/SmartProgram.cs","ec":int32(70) + } + push int32(2) + dupn + push int8(14) + cast + push int32(4) + dupn + concat + sput + pop + pop + push x42616C616E636573 + push int32(4) + dupn + push x42616C616E636573 + push int32(6) + dupn + push int32(0) + call @stdlib_storage_get_default + push int32(5) + dupn + add + meta source_mark { + "sl":int32(22),"sc":int32(5),"el":int32(22),"src":"$PRAVDA_TMP_DIR/SmartProgram.cs","ec":int32(6) + } + push int32(2) + dupn + push int8(14) + cast + push int32(4) + dupn + concat + sput + pop + pop + @Transfer_br86: + jump @Transfer_lvc + @Transfer_lvc: + pop + pop + pop + jump @stop + @method_ctor: + meta method { + "name":"ctor","returnTpe":int8(0) + } + push "init" + sexist + not + jumpi @ctor_ok + push "Program has been already initialized" + throw + @ctor_ok: + push null + push "init" + sput + meta source_mark { + "sl":int32(7),"sc":int32(5),"el":int32(7),"src":"$PRAVDA_TMP_DIR/SmartProgram.cs","ec":int32(70) + } + jump @ctor_lvc + @ctor_lvc: + pop + jump @stop + @stdlib_storage_get_default: + push int32(2) + dupn + push int8(14) + cast + push int32(4) + dupn + concat + sexist + jumpi @get_default_if + swap + pop + swap + pop + ret + @get_default_if: + pop + push int8(14) + cast + swap + concat + sget + ret + @stop: diff --git a/dotnet/src/test/resources/translation/StaticClass.trs b/dotnet/src/test/resources/translation/StaticClass.trs new file mode 100644 index 00000000..2a2c6ce0 --- /dev/null +++ b/dotnet/src/test/resources/translation/StaticClass.trs @@ -0,0 +1,550 @@ +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: StaticClass.exe + sources: + - Pravda.dll + - dotnet-tests/resources/StaticClass.cs + optimize: true +--- +translation: |- + meta custom "CIL" + meta program_name "StaticClass" + dup + push "ctor" + eq + jumpi @method_ctor + push "init" + sexist + jumpi @methods + push "Program was not initialized" + throw + @methods: + dup + push "TestToHex" + eq + jumpi @method_TestToHex + push "Wrong method name" + throw + @method_TestToHex: + meta method { + "name":"TestToHex",int32(0):int8(14),"returnTpe":int8(11) + } + meta source_mark { + "sl":int32(67),"sc":int32(9),"el":int32(67),"src":"$PRAVDA_TMP_DIR/StaticClass.cs","ec":int32(43) + } + push int32(2) + dupn + call @func_StaticClassUtils.StringUtils.BytesToHex_Expload.Pravda.Bytes + jump @TestToHex_lvc + @TestToHex_lvc: + swap + pop + swap + pop + jump @stop + @method_ctor: + meta method { + "name":"ctor","returnTpe":int8(0) + } + push "init" + sexist + not + jumpi @ctor_ok + push "Program has been already initialized" + throw + @ctor_ok: + push null + push "init" + sput + jump @ctor_lvc + @ctor_lvc: + pop + jump @stop + @func_StaticClassUtils.StringUtils.ByteToHex_int32: + meta source_mark { + "sl":int32(48),"sc":int32(13),"el":int32(48),"src":"$PRAVDA_TMP_DIR/StaticClass.cs","ec":int32(54) + } + push int32(1) + dupn + push int32(16) + swap + div + call @func_StaticClassUtils.StringUtils.HexPart_int32 + push int32(2) + dupn + push int32(16) + swap + mod + call @func_StaticClassUtils.StringUtils.HexPart_int32 + swap + concat + jump @StaticClassUtils.StringUtils.ByteToHex_int32_lvc + @StaticClassUtils.StringUtils.ByteToHex_int32_lvc: + swap + pop + ret + @func_StaticClassUtils.StringUtils.BytesToHex_Expload.Pravda.Bytes: + push null + push null + meta source_mark { + "sl":int32(53),"sc":int32(13),"el":int32(53),"src":"$PRAVDA_TMP_DIR/StaticClass.cs","ec":int32(29) + } + push "" + push int32(3) + swapn + pop + meta source_mark { + "sl":int32(54),"sc":int32(18),"el":int32(54),"src":"$PRAVDA_TMP_DIR/StaticClass.cs","ec":int32(27) + } + push int32(0) + push int32(2) + swapn + pop + jump @StaticClassUtils.StringUtils.BytesToHex_Expload.Pravda.Bytes_br39 + @StaticClassUtils.StringUtils.BytesToHex_Expload.Pravda.Bytes_br10: + push int32(2) + dupn + push int32(4) + dupn + meta source_mark { + "sl":int32(55),"sc":int32(17),"el":int32(55),"src":"$PRAVDA_TMP_DIR/StaticClass.cs","ec":int32(51) + } + push int32(3) + dupn + array_get + push int32(255) + and + call @func_StaticClassUtils.StringUtils.ByteToHex_int32 + swap + concat + push int32(3) + swapn + pop + push int32(1) + dupn + push int32(1) + meta source_mark { + "sl":int32(54),"sc":int32(49),"el":int32(54),"src":"$PRAVDA_TMP_DIR/StaticClass.cs","ec":int32(52) + } + add + push int32(2) + swapn + pop + @StaticClassUtils.StringUtils.BytesToHex_Expload.Pravda.Bytes_br39: + push int32(1) + dupn + push int32(4) + dupn + meta source_mark { + "sl":int32(54),"sc":int32(29),"el":int32(54),"src":"$PRAVDA_TMP_DIR/StaticClass.cs","ec":int32(47) + } + length + swap + lt + push int8(3) + cast + push int32(0) + eq + not + jumpi @StaticClassUtils.StringUtils.BytesToHex_Expload.Pravda.Bytes_br10 + push int32(2) + dupn + jump @StaticClassUtils.StringUtils.BytesToHex_Expload.Pravda.Bytes_lvc + @StaticClassUtils.StringUtils.BytesToHex_Expload.Pravda.Bytes_lvc: + swap + pop + swap + pop + swap + pop + ret + @func_StaticClassUtils.StringUtils.HexPart_int32: + meta source_mark { + "sl":int32(11),"sc":int32(13),"el":int32(11),"src":"$PRAVDA_TMP_DIR/StaticClass.cs","ec":int32(24) + } + push int32(1) + dupn + push int32(0) + eq + not + jumpi @StaticClassUtils.StringUtils.HexPart_int32_br9 + meta source_mark { + "sl":int32(12),"sc":int32(17),"el":int32(12),"src":"$PRAVDA_TMP_DIR/StaticClass.cs","ec":int32(28) + } + push "0" + jump @StaticClassUtils.StringUtils.HexPart_int32_lvc + @StaticClassUtils.StringUtils.HexPart_int32_br9: + push int32(1) + dupn + push int32(1) + meta source_mark { + "sl":int32(13),"sc":int32(18),"el":int32(13),"src":"$PRAVDA_TMP_DIR/StaticClass.cs","ec":int32(29) + } + eq + push int8(3) + cast + push int8(9) + cast + not + push int8(3) + cast + push int32(0) + eq + not + jumpi @StaticClassUtils.StringUtils.HexPart_int32_br19 + meta source_mark { + "sl":int32(14),"sc":int32(17),"el":int32(14),"src":"$PRAVDA_TMP_DIR/StaticClass.cs","ec":int32(28) + } + push "1" + jump @StaticClassUtils.StringUtils.HexPart_int32_lvc + @StaticClassUtils.StringUtils.HexPart_int32_br19: + push int32(1) + dupn + meta source_mark { + "sl":int32(15),"sc":int32(18),"el":int32(15),"src":"$PRAVDA_TMP_DIR/StaticClass.cs","ec":int32(29) + } + push int32(2) + eq + push int8(3) + cast + push int8(9) + cast + not + push int8(3) + cast + push int32(0) + eq + not + jumpi @StaticClassUtils.StringUtils.HexPart_int32_br29 + meta source_mark { + "sl":int32(16),"sc":int32(17),"el":int32(16),"src":"$PRAVDA_TMP_DIR/StaticClass.cs","ec":int32(28) + } + push "2" + jump @StaticClassUtils.StringUtils.HexPart_int32_lvc + @StaticClassUtils.StringUtils.HexPart_int32_br29: + meta source_mark { + "sl":int32(17),"sc":int32(18),"el":int32(17),"src":"$PRAVDA_TMP_DIR/StaticClass.cs","ec":int32(29) + } + push int32(1) + dupn + push int32(3) + eq + push int8(3) + cast + meta source_mark { + "sl":int32(18),"sc":int32(17),"el":int32(18),"src":"$PRAVDA_TMP_DIR/StaticClass.cs","ec":int32(28) + } + push int8(9) + cast + not + push int8(3) + cast + push int32(0) + eq + not + jumpi @StaticClassUtils.StringUtils.HexPart_int32_br39 + push "3" + meta source_mark { + "sl":int32(19),"sc":int32(18),"el":int32(19),"src":"$PRAVDA_TMP_DIR/StaticClass.cs","ec":int32(29) + } + jump @StaticClassUtils.StringUtils.HexPart_int32_lvc + @StaticClassUtils.StringUtils.HexPart_int32_br39: + push int32(1) + dupn + push int32(4) + meta source_mark { + "sl":int32(20),"sc":int32(17),"el":int32(20),"src":"$PRAVDA_TMP_DIR/StaticClass.cs","ec":int32(28) + } + eq + push int8(3) + cast + push int8(9) + cast + not + push int8(3) + cast + push int32(0) + eq + not + jumpi @StaticClassUtils.StringUtils.HexPart_int32_br49 + meta source_mark { + "sl":int32(21),"sc":int32(18),"el":int32(21),"src":"$PRAVDA_TMP_DIR/StaticClass.cs","ec":int32(29) + } + push "4" + jump @StaticClassUtils.StringUtils.HexPart_int32_lvc + @StaticClassUtils.StringUtils.HexPart_int32_br49: + push int32(1) + dupn + push int32(5) + meta source_mark { + "sl":int32(22),"sc":int32(17),"el":int32(22),"src":"$PRAVDA_TMP_DIR/StaticClass.cs","ec":int32(28) + } + eq + push int8(3) + cast + push int8(9) + cast + not + push int8(3) + cast + push int32(0) + eq + not + jumpi @StaticClassUtils.StringUtils.HexPart_int32_br59 + meta source_mark { + "sl":int32(23),"sc":int32(18),"el":int32(23),"src":"$PRAVDA_TMP_DIR/StaticClass.cs","ec":int32(29) + } + push "5" + jump @StaticClassUtils.StringUtils.HexPart_int32_lvc + @StaticClassUtils.StringUtils.HexPart_int32_br59: + push int32(1) + dupn + meta source_mark { + "sl":int32(24),"sc":int32(17),"el":int32(24),"src":"$PRAVDA_TMP_DIR/StaticClass.cs","ec":int32(28) + } + push int32(6) + eq + push int8(3) + cast + push int8(9) + cast + not + push int8(3) + cast + push int32(0) + eq + not + jumpi @StaticClassUtils.StringUtils.HexPart_int32_br69 + meta source_mark { + "sl":int32(25),"sc":int32(18),"el":int32(25),"src":"$PRAVDA_TMP_DIR/StaticClass.cs","ec":int32(29) + } + push "6" + jump @StaticClassUtils.StringUtils.HexPart_int32_lvc + @StaticClassUtils.StringUtils.HexPart_int32_br69: + meta source_mark { + "sl":int32(26),"sc":int32(17),"el":int32(26),"src":"$PRAVDA_TMP_DIR/StaticClass.cs","ec":int32(28) + } + push int32(1) + dupn + push int32(7) + eq + push int8(3) + cast + push int8(9) + cast + not + push int8(3) + cast + push int32(0) + eq + not + jumpi @StaticClassUtils.StringUtils.HexPart_int32_br79 + meta source_mark { + "sl":int32(27),"sc":int32(18),"el":int32(27),"src":"$PRAVDA_TMP_DIR/StaticClass.cs","ec":int32(29) + } + push "7" + meta source_mark { + "sl":int32(28),"sc":int32(17),"el":int32(28),"src":"$PRAVDA_TMP_DIR/StaticClass.cs","ec":int32(28) + } + jump @StaticClassUtils.StringUtils.HexPart_int32_lvc + @StaticClassUtils.StringUtils.HexPart_int32_br79: + push int32(1) + dupn + push int32(8) + eq + push int8(3) + cast + push int8(9) + cast + not + push int8(3) + cast + push int32(0) + eq + not + jumpi @StaticClassUtils.StringUtils.HexPart_int32_br89 + meta source_mark { + "sl":int32(29),"sc":int32(18),"el":int32(29),"src":"$PRAVDA_TMP_DIR/StaticClass.cs","ec":int32(29) + } + push "8" + meta source_mark { + "sl":int32(30),"sc":int32(17),"el":int32(30),"src":"$PRAVDA_TMP_DIR/StaticClass.cs","ec":int32(28) + } + jump @StaticClassUtils.StringUtils.HexPart_int32_lvc + @StaticClassUtils.StringUtils.HexPart_int32_br89: + push int32(1) + dupn + push int32(9) + eq + push int8(3) + cast + meta source_mark { + "sl":int32(31),"sc":int32(18),"el":int32(31),"src":"$PRAVDA_TMP_DIR/StaticClass.cs","ec":int32(30) + } + push int8(9) + cast + not + push int8(3) + cast + push int32(0) + eq + not + jumpi @StaticClassUtils.StringUtils.HexPart_int32_br100 + meta source_mark { + "sl":int32(32),"sc":int32(17),"el":int32(32),"src":"$PRAVDA_TMP_DIR/StaticClass.cs","ec":int32(28) + } + push "9" + jump @StaticClassUtils.StringUtils.HexPart_int32_lvc + @StaticClassUtils.StringUtils.HexPart_int32_br100: + push int32(1) + dupn + push int32(10) + meta source_mark { + "sl":int32(33),"sc":int32(18),"el":int32(33),"src":"$PRAVDA_TMP_DIR/StaticClass.cs","ec":int32(30) + } + eq + push int8(3) + cast + push int8(9) + cast + not + push int8(3) + cast + push int32(0) + eq + not + jumpi @StaticClassUtils.StringUtils.HexPart_int32_br111 + meta source_mark { + "sl":int32(34),"sc":int32(17),"el":int32(34),"src":"$PRAVDA_TMP_DIR/StaticClass.cs","ec":int32(28) + } + push "A" + jump @StaticClassUtils.StringUtils.HexPart_int32_lvc + @StaticClassUtils.StringUtils.HexPart_int32_br111: + push int32(1) + dupn + push int32(11) + meta source_mark { + "sl":int32(35),"sc":int32(18),"el":int32(35),"src":"$PRAVDA_TMP_DIR/StaticClass.cs","ec":int32(30) + } + eq + push int8(3) + cast + push int8(9) + cast + not + push int8(3) + cast + push int32(0) + eq + not + jumpi @StaticClassUtils.StringUtils.HexPart_int32_br122 + meta source_mark { + "sl":int32(36),"sc":int32(17),"el":int32(36),"src":"$PRAVDA_TMP_DIR/StaticClass.cs","ec":int32(28) + } + push "B" + jump @StaticClassUtils.StringUtils.HexPart_int32_lvc + @StaticClassUtils.StringUtils.HexPart_int32_br122: + push int32(1) + dupn + meta source_mark { + "sl":int32(37),"sc":int32(18),"el":int32(37),"src":"$PRAVDA_TMP_DIR/StaticClass.cs","ec":int32(30) + } + push int32(12) + eq + push int8(3) + cast + push int8(9) + cast + not + push int8(3) + cast + push int32(0) + eq + not + jumpi @StaticClassUtils.StringUtils.HexPart_int32_br133 + meta source_mark { + "sl":int32(38),"sc":int32(17),"el":int32(38),"src":"$PRAVDA_TMP_DIR/StaticClass.cs","ec":int32(28) + } + push "C" + jump @StaticClassUtils.StringUtils.HexPart_int32_lvc + @StaticClassUtils.StringUtils.HexPart_int32_br133: + push int32(1) + dupn + meta source_mark { + "sl":int32(39),"sc":int32(18),"el":int32(39),"src":"$PRAVDA_TMP_DIR/StaticClass.cs","ec":int32(30) + } + push int32(13) + eq + push int8(3) + cast + push int8(9) + cast + not + push int8(3) + cast + push int32(0) + eq + not + jumpi @StaticClassUtils.StringUtils.HexPart_int32_br144 + meta source_mark { + "sl":int32(40),"sc":int32(17),"el":int32(40),"src":"$PRAVDA_TMP_DIR/StaticClass.cs","ec":int32(28) + } + push "D" + jump @StaticClassUtils.StringUtils.HexPart_int32_lvc + @StaticClassUtils.StringUtils.HexPart_int32_br144: + meta source_mark { + "sl":int32(41),"sc":int32(18),"el":int32(41),"src":"$PRAVDA_TMP_DIR/StaticClass.cs","ec":int32(30) + } + push int32(1) + dupn + push int32(14) + eq + push int8(3) + cast + meta source_mark { + "sl":int32(42),"sc":int32(17),"el":int32(42),"src":"$PRAVDA_TMP_DIR/StaticClass.cs","ec":int32(28) + } + push int8(9) + cast + not + push int8(3) + cast + push int32(0) + eq + not + jumpi @StaticClassUtils.StringUtils.HexPart_int32_br155 + push "E" + meta source_mark { + "sl":int32(43),"sc":int32(13),"el":int32(43),"src":"$PRAVDA_TMP_DIR/StaticClass.cs","ec":int32(23) + } + jump @StaticClassUtils.StringUtils.HexPart_int32_lvc + @StaticClassUtils.StringUtils.HexPart_int32_br155: + push int32(1) + dupn + push int32(15) + eq + push int8(3) + cast + push int8(9) + cast + not + push int8(3) + cast + push int32(0) + eq + not + jumpi @StaticClassUtils.StringUtils.HexPart_int32_br166 + push "F" + jump @StaticClassUtils.StringUtils.HexPart_int32_lvc + @StaticClassUtils.StringUtils.HexPart_int32_br166: + push "" + jump @StaticClassUtils.StringUtils.HexPart_int32_lvc + @StaticClassUtils.StringUtils.HexPart_int32_lvc: + swap + pop + ret + @stop: diff --git a/dotnet/src/test/resources/translation/stdlib.trs b/dotnet/src/test/resources/translation/Stdlib.trs similarity index 59% rename from dotnet/src/test/resources/translation/stdlib.trs rename to dotnet/src/test/resources/translation/Stdlib.trs index 1ad19429..87b3f440 100644 --- a/dotnet/src/test/resources/translation/stdlib.trs +++ b/dotnet/src/test/resources/translation/Stdlib.trs @@ -1,7 +1,18 @@ -exe: stdlib.exe +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: Stdlib.exe + sources: + - Pravda.dll + - dotnet-tests/resources/Stdlib.cs + optimize: true --- translation: |- meta custom "CIL" + meta program_name "Stdlib" dup push "ctor" eq @@ -13,6 +24,10 @@ translation: |- throw @methods: dup + push "BytesToHex" + eq + jumpi @method_BytesToHex + dup push "Ripemd160" eq jumpi @method_Ripemd160 @@ -22,47 +37,57 @@ translation: |- jumpi @method_ValidateEd25519Signature push "Wrong method name" throw + @method_BytesToHex: + meta method { + "name":"BytesToHex",int32(0):int8(14),"returnTpe":int8(11) + } + meta source_mark { + "sl":int32(19),"sc":int32(9),"el":int32(19),"src":"$PRAVDA_TMP_DIR/Stdlib.cs","ec":int32(41) + } + push int32(2) + dupn + push int32(5) + scall + jump @BytesToHex_lvc + @BytesToHex_lvc: + swap + pop + swap + pop + jump @stop @method_Ripemd160: meta method { "name":"Ripemd160",int32(0):int8(11),"returnTpe":int8(14) } - push null - push int32(3) + meta source_mark { + "sl":int32(9),"sc":int32(9),"el":int32(9),"src":"$PRAVDA_TMP_DIR/Stdlib.cs","ec":int32(40) + } + push int32(2) dupn push int32(2) scall - push int32(2) - swapn - pop - push int32(1) - dupn jump @Ripemd160_lvc @Ripemd160_lvc: swap pop swap pop - swap - pop jump @stop @method_ValidateEd25519Signature: meta method { "name":"ValidateEd25519Signature",int32(1):int8(11),int32(0):int8(14),"returnTpe":int8(9),int32(2):int8(14) } - push null - push int32(5) + meta source_mark { + "sl":int32(14),"sc":int32(9),"el":int32(14),"src":"$PRAVDA_TMP_DIR/Stdlib.cs","ec":int32(71) + } + push int32(4) dupn - push int32(5) + push int32(4) dupn - push int32(5) + push int32(4) dupn push int32(1) scall - push int32(2) - swapn - pop - push int32(1) - dupn jump @ValidateEd25519Signature_lvc @ValidateEd25519Signature_lvc: swap @@ -73,8 +98,6 @@ translation: |- pop swap pop - swap - pop jump @stop @method_ctor: meta method { diff --git a/dotnet/src/test/resources/translation/Strings.trs b/dotnet/src/test/resources/translation/Strings.trs new file mode 100644 index 00000000..f87da01f --- /dev/null +++ b/dotnet/src/test/resources/translation/Strings.trs @@ -0,0 +1,180 @@ +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: Strings.exe + sources: + - Pravda.dll + - dotnet-tests/resources/Strings.cs + optimize: true +--- +translation: |- + meta custom "CIL" + meta program_name "Strings" + dup + push "ctor" + eq + jumpi @method_ctor + push "init" + sexist + jumpi @methods + push "Program was not initialized" + throw + @methods: + dup + push "TestStrings" + eq + jumpi @method_TestStrings + push "Wrong method name" + throw + @method_TestStrings: + meta method { + "name":"TestStrings","returnTpe":int8(0) + } + push null + push null + push null + meta source_mark { + "sl":int32(11),"sc":int32(9),"el":int32(11),"src":"$PRAVDA_TMP_DIR/Strings.cs","ec":int32(40) + } + push "zauser1" + push int32(4) + swapn + pop + meta source_mark { + "sl":int32(12),"sc":int32(9),"el":int32(12),"src":"$PRAVDA_TMP_DIR/Strings.cs","ec":int32(26) + } + push "us" + meta source_mark { + "sl":int32(13),"sc":int32(9),"el":int32(13),"src":"$PRAVDA_TMP_DIR/Strings.cs","ec":int32(28) + } + push "er2" + push int32(4) + swapn + pop + meta source_mark { + "sl":int32(14),"sc":int32(9),"el":int32(14),"src":"$PRAVDA_TMP_DIR/Strings.cs","ec":int32(32) + } + push int32(3) + dupn + swap + concat + push int32(2) + swapn + pop + meta source_mark { + "sl":int32(16),"sc":int32(9),"el":int32(16),"src":"$PRAVDA_TMP_DIR/Strings.cs","ec":int32(39) + } + push x537472696E67734D617070696E67 + push int32(2) + dupn + push int32(5) + dupn + push int32(2) + dupn + push int8(14) + cast + push int32(4) + dupn + concat + sput + pop + pop + meta source_mark { + "sl":int32(17),"sc":int32(9),"el":int32(17),"src":"$PRAVDA_TMP_DIR/Strings.cs","ec":int32(49) + } + push x537472696E67734D617070696E67 + push "user1" + push int8(14) + cast + swap + concat + sexist + push int8(3) + cast + push int8(9) + cast + not + push int8(3) + cast + push int32(0) + eq + not + jumpi @TestStrings_br76 + meta source_mark { + "sl":int32(18),"sc":int32(11),"el":int32(18),"src":"$PRAVDA_TMP_DIR/Strings.cs","ec":int32(40) + } + push x537472696E67734D617070696E67 + push "user2" + push "" + push int32(2) + dupn + push int8(14) + cast + push int32(4) + dupn + concat + sput + pop + pop + @TestStrings_br76: + push int32(3) + dupn + meta source_mark { + "sl":int32(21),"sc":int32(9),"el":int32(21),"src":"$PRAVDA_TMP_DIR/Strings.cs","ec":int32(29) + } + push int32(0) + array_get + pop + push int32(1) + dupn + meta source_mark { + "sl":int32(22),"sc":int32(9),"el":int32(22),"src":"$PRAVDA_TMP_DIR/Strings.cs","ec":int32(27) + } + push int32(3) + array_get + pop + push int32(1) + dupn + meta source_mark { + "sl":int32(23),"sc":int32(9),"el":int32(23),"src":"$PRAVDA_TMP_DIR/Strings.cs","ec":int32(42) + } + push int32(1) + push int32(2) + push int32(2) + dupn + add + swap + slice + pop + jump @TestStrings_lvc + @TestStrings_lvc: + pop + pop + pop + pop + jump @stop + @method_ctor: + meta method { + "name":"ctor","returnTpe":int8(0) + } + push "init" + sexist + not + jumpi @ctor_ok + push "Program has been already initialized" + throw + @ctor_ok: + push null + push "init" + sput + meta source_mark { + "sl":int32(7),"sc":int32(5),"el":int32(7),"src":"$PRAVDA_TMP_DIR/Strings.cs","ec":int32(84) + } + jump @ctor_lvc + @ctor_lvc: + pop + jump @stop + @stop: diff --git a/dotnet/src/test/resources/translation/SystemMethods.trs b/dotnet/src/test/resources/translation/SystemMethods.trs new file mode 100644 index 00000000..273f86c3 --- /dev/null +++ b/dotnet/src/test/resources/translation/SystemMethods.trs @@ -0,0 +1,88 @@ +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: SystemMethods.exe + sources: + - Pravda.dll + - dotnet-tests/resources/SystemMethods.cs + optimize: true +--- +translation: |- + meta custom "CIL" + meta program_name "SystemMethods" + dup + push "ctor" + eq + jumpi @method_ctor + push "init" + sexist + jumpi @methods + push "Program was not initialized" + throw + @methods: + dup + push "TestSystemMethods" + eq + jumpi @method_TestSystemMethods + push "Wrong method name" + throw + @method_TestSystemMethods: + meta method { + "name":"TestSystemMethods","returnTpe":int8(0) + } + meta source_mark { + "sl":int32(9),"sc":int32(9),"el":int32(9),"src":"$PRAVDA_TMP_DIR/SystemMethods.cs","ec":int32(57) + } + push x0000000000000000000000000000000000000000000000000000000000000000 + balance + pop + meta source_mark { + "sl":int32(10),"sc":int32(9),"el":int32(10),"src":"$PRAVDA_TMP_DIR/SystemMethods.cs","ec":int32(54) + } + paddr + pop + meta source_mark { + "sl":int32(12),"sc":int32(9),"el":int32(12),"src":"$PRAVDA_TMP_DIR/SystemMethods.cs","ec":int32(52) + } + push x0000000000000000000000000000000000000000000000000000000000000000 + push int32(100) + push int8(5) + cast + transfer + meta source_mark { + "sl":int32(13),"sc":int32(9),"el":int32(13),"src":"$PRAVDA_TMP_DIR/SystemMethods.cs","ec":int32(63) + } + push x0000000000000000000000000000000000000000000000000000000000000000 + push int32(200) + push int8(5) + cast + ptransfer + meta source_mark { + "sl":int32(16),"sc":int32(5),"el":int32(16),"src":"$PRAVDA_TMP_DIR/SystemMethods.cs","ec":int32(6) + } + jump @TestSystemMethods_lvc + @TestSystemMethods_lvc: + pop + jump @stop + @method_ctor: + meta method { + "name":"ctor","returnTpe":int8(0) + } + push "init" + sexist + not + jumpi @ctor_ok + push "Program has been already initialized" + throw + @ctor_ok: + push null + push "init" + sput + jump @ctor_lvc + @ctor_lvc: + pop + jump @stop + @stop: diff --git a/dotnet/src/test/resources/translation/VmOps.trs b/dotnet/src/test/resources/translation/VmOps.trs new file mode 100644 index 00000000..c3077af4 --- /dev/null +++ b/dotnet/src/test/resources/translation/VmOps.trs @@ -0,0 +1,66 @@ +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: VmOps.exe + sources: + - Pravda.dll + - dotnet-tests/resources/VmOps.cs + optimize: true +--- +translation: |- + meta custom "CIL" + meta program_name "VmOps" + dup + push "ctor" + eq + jumpi @method_ctor + push "init" + sexist + jumpi @methods + push "Program was not initialized" + throw + @methods: + dup + push "TestThrow" + eq + jumpi @method_TestThrow + push "Wrong method name" + throw + @method_TestThrow: + meta method { + "name":"TestThrow","returnTpe":int8(0) + } + meta source_mark { + "sl":int32(9),"sc":int32(9),"el":int32(9),"src":"$PRAVDA_TMP_DIR/VmOps.cs","ec":int32(30) + } + push "Oops!" + throw + meta source_mark { + "sl":int32(10),"sc":int32(5),"el":int32(10),"src":"$PRAVDA_TMP_DIR/VmOps.cs","ec":int32(6) + } + jump @TestThrow_lvc + @TestThrow_lvc: + pop + jump @stop + @method_ctor: + meta method { + "name":"ctor","returnTpe":int8(0) + } + push "init" + sexist + not + jumpi @ctor_ok + push "Program has been already initialized" + throw + @ctor_ok: + push null + push "init" + sput + jump @ctor_lvc + @ctor_lvc: + pop + jump @stop + @stop: diff --git a/dotnet/src/test/resources/translation/zoo_program.trs b/dotnet/src/test/resources/translation/ZooProgram.trs similarity index 55% rename from dotnet/src/test/resources/translation/zoo_program.trs rename to dotnet/src/test/resources/translation/ZooProgram.trs index 2f58c588..4b8a3e56 100644 --- a/dotnet/src/test/resources/translation/zoo_program.trs +++ b/dotnet/src/test/resources/translation/ZooProgram.trs @@ -1,7 +1,18 @@ -exe: zoo_program.exe +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: ZooProgram.exe + sources: + - Pravda.dll + - dotnet-tests/resources/ZooProgram.cs + optimize: true --- translation: |- meta custom "CIL" + meta program_name "ZooProgram" dup push "ctor" eq @@ -39,10 +50,11 @@ translation: |- "name":"BreedPets",int32(1):int8(11),int32(0):int8(11),"returnTpe":int8(11) } push null - push null - push null + meta source_mark { + "sl":int32(59),"sc":int32(9),"el":int32(61),"src":"$PRAVDA_TMP_DIR/ZooProgram.cs","ec":int32(84) + } push x506574546F4F776E6572 - push int32(7) + push int32(5) dupn push x call @stdlib_storage_get_default @@ -55,11 +67,12 @@ translation: |- not push int8(3) cast - push int32(1) + push int32(0) eq - jumpi @BreedPets_br79 + not + jumpi @BreedPets_br144 push x506574546F4F776E6572 - push int32(6) + push int32(4) dupn push x call @stdlib_storage_get_default @@ -72,52 +85,54 @@ translation: |- not push int8(3) cast - push int32(1) + push int32(0) eq - jumpi @BreedPets_br79 + not + jumpi @BreedPets_br144 push x506574546F5A6F6F - push int32(7) + push int32(5) dupn push int32(-1) call @stdlib_storage_get_default push x506574546F5A6F6F - push int32(7) + push int32(5) dupn push int32(-1) call @stdlib_storage_get_default + meta source_mark { + "sl":int32(63),"sc":int32(13),"el":int32(63),"src":"$PRAVDA_TMP_DIR/ZooProgram.cs","ec":int32(41) + } eq push int8(3) cast - jump @BreedPets_br80 - @BreedPets_br79: - push int32(0) - @BreedPets_br80: - push int32(4) - swapn - pop - push int32(3) - dupn push int8(9) cast not push int8(3) cast - push int32(1) + push int32(0) eq - jumpi @BreedPets_br157 - push int32(6) + not + jumpi @BreedPets_br144 + push int32(4) dupn - push int32(6) + push int32(4) dupn + meta source_mark { + "sl":int32(64),"sc":int32(13),"el":int32(64),"src":"$PRAVDA_TMP_DIR/ZooProgram.cs","ec":int32(48) + } swap concat - push int32(3) + push int32(2) swapn pop push x506574546F4F776E6572 - push int32(3) + push int32(2) dupn from + meta source_mark { + "sl":int32(65),"sc":int32(13),"el":int32(65),"src":"$PRAVDA_TMP_DIR/ZooProgram.cs","ec":int32(82) + } push int32(2) dupn push int8(14) @@ -129,10 +144,10 @@ translation: |- pop pop push x5065745369676E6174757265 - push int32(3) + push int32(2) dupn push x5065745369676E6174757265 - push int32(9) + push int32(7) dupn push int8(14) cast @@ -140,7 +155,7 @@ translation: |- concat sget push x5065745369676E6174757265 - push int32(9) + push int32(7) dupn push int8(14) cast @@ -149,6 +164,12 @@ translation: |- sget swap concat + meta source_mark { + "sl":int32(66),"sc":int32(13),"el":int32(66),"src":"$PRAVDA_TMP_DIR/ZooProgram.cs","ec":int32(27) + } + meta source_mark { + "sl":int32(68),"sc":int32(13),"el":int32(68),"src":"$PRAVDA_TMP_DIR/ZooProgram.cs","ec":int32(23) + } push int32(2) dupn push int8(14) @@ -159,21 +180,12 @@ translation: |- sput pop pop - push int32(2) - dupn - push int32(2) - swapn - pop - jump @BreedPets_br166 - @BreedPets_br157: - push "" - push int32(2) - swapn - pop - @BreedPets_br166: push int32(1) dupn jump @BreedPets_lvc + @BreedPets_br144: + push "" + jump @BreedPets_lvc @BreedPets_lvc: swap pop @@ -183,20 +195,17 @@ translation: |- pop swap pop - swap - pop - swap - pop jump @stop @method_NewPet: meta method { "name":"NewPet",int32(0):int8(3),"returnTpe":int8(11) } push null - push null - push null + meta source_mark { + "sl":int32(39),"sc":int32(9),"el":int32(39),"src":"$PRAVDA_TMP_DIR/ZooProgram.cs","ec":int32(72) + } push x5A6F6F546F4F776E6572 - push int32(6) + push int32(4) dupn push x call @stdlib_storage_get_default @@ -204,19 +213,18 @@ translation: |- eq push int8(3) cast - push int32(4) - swapn - pop - push int32(3) - dupn + meta source_mark { + "sl":int32(40),"sc":int32(13),"el":int32(40),"src":"$PRAVDA_TMP_DIR/ZooProgram.cs","ec":int32(65) + } push int8(9) cast not push int8(3) cast - push int32(1) + push int32(0) eq - jumpi @NewPet_br108 + not + jumpi @NewPet_br98 push "pet" push "p_PetId" sget @@ -224,13 +232,19 @@ translation: |- cast swap concat - push int32(3) + meta source_mark { + "sl":int32(41),"sc":int32(13),"el":int32(41),"src":"$PRAVDA_TMP_DIR/ZooProgram.cs","ec":int32(45) + } + push int32(2) swapn pop push x506574546F4F776E6572 - push int32(3) + push int32(2) dupn from + meta source_mark { + "sl":int32(42),"sc":int32(13),"el":int32(42),"src":"$PRAVDA_TMP_DIR/ZooProgram.cs","ec":int32(56) + } push int32(2) dupn push int8(14) @@ -242,11 +256,14 @@ translation: |- pop pop push x5065745369676E6174757265 - push int32(3) + push int32(2) dupn - push int32(4) + push int32(3) dupn call @func_GenerateSignature + meta source_mark { + "sl":int32(43),"sc":int32(13),"el":int32(43),"src":"$PRAVDA_TMP_DIR/ZooProgram.cs","ec":int32(24) + } push int32(2) dupn push int8(14) @@ -261,22 +278,19 @@ translation: |- sget push int32(1) add + meta source_mark { + "sl":int32(44),"sc":int32(13),"el":int32(44),"src":"$PRAVDA_TMP_DIR/ZooProgram.cs","ec":int32(24) + } push "p_PetId" sput - push int32(2) - dupn - push int32(2) - swapn - pop - jump @NewPet_br116 - @NewPet_br108: - push "" - push int32(2) - swapn - pop - @NewPet_br116: push int32(1) dupn + meta source_mark { + "sl":int32(46),"sc":int32(9),"el":int32(46),"src":"$PRAVDA_TMP_DIR/ZooProgram.cs","ec":int32(19) + } + jump @NewPet_lvc + @NewPet_br98: + push "" jump @NewPet_lvc @NewPet_lvc: swap @@ -285,16 +299,14 @@ translation: |- pop swap pop - swap - pop - swap - pop jump @stop @method_NewZoo: meta method { "name":"NewZoo","returnTpe":int8(3) } - push null + meta source_mark { + "sl":int32(25),"sc":int32(9),"el":int32(25),"src":"$PRAVDA_TMP_DIR/ZooProgram.cs","ec":int32(44) + } push x5A6F6F546F4F776E6572 push "p_ZooCnt" sget @@ -309,37 +321,38 @@ translation: |- sput pop pop + meta source_mark { + "sl":int32(26),"sc":int32(9),"el":int32(26),"src":"$PRAVDA_TMP_DIR/ZooProgram.cs","ec":int32(21) + } push "p_ZooCnt" sget push int32(1) add push "p_ZooCnt" sput + meta source_mark { + "sl":int32(27),"sc":int32(9),"el":int32(27),"src":"$PRAVDA_TMP_DIR/ZooProgram.cs","ec":int32(27) + } push "p_ZooCnt" sget push int32(1) push int32(-1) mul add - push int32(2) - swapn - pop - push int32(1) - dupn jump @NewZoo_lvc @NewZoo_lvc: swap pop - swap - pop jump @stop @method_TransferPet: meta method { "name":"TransferPet",int32(1):int8(3),int32(0):int8(14),"returnTpe":int8(0),int32(2):int8(11) } - push null + meta source_mark { + "sl":int32(51),"sc":int32(9),"el":int32(51),"src":"$PRAVDA_TMP_DIR/ZooProgram.cs","ec":int32(123) + } push x506574546F4F776E6572 - push int32(4) + push int32(3) dupn push x call @stdlib_storage_get_default @@ -352,41 +365,40 @@ translation: |- not push int8(3) cast - push int32(1) + push int32(0) eq - jumpi @TransferPet_br47 + not + jumpi @TransferPet_br70 push x5A6F6F546F4F776E6572 - push int32(5) + push int32(4) dupn push x call @stdlib_storage_get_default - push int32(6) + push int32(5) dupn + meta source_mark { + "sl":int32(52),"sc":int32(12),"el":int32(52),"src":"$PRAVDA_TMP_DIR/ZooProgram.cs","ec":int32(33) + } eq push int8(3) cast - jump @TransferPet_br48 - @TransferPet_br47: - push int32(0) - @TransferPet_br48: - push int32(2) - swapn - pop - push int32(1) - dupn push int8(9) cast not push int8(3) cast - push int32(1) + push int32(0) eq - jumpi @TransferPet_br82 + not + jumpi @TransferPet_br70 push x506574546F4F776E6572 - push int32(4) + push int32(3) dupn - push int32(7) + push int32(6) dupn + meta source_mark { + "sl":int32(53),"sc":int32(12),"el":int32(53),"src":"$PRAVDA_TMP_DIR/ZooProgram.cs","ec":int32(32) + } push int32(2) dupn push int8(14) @@ -398,10 +410,13 @@ translation: |- pop pop push x506574546F5A6F6F - push int32(4) + push int32(3) dupn - push int32(6) + push int32(5) dupn + meta source_mark { + "sl":int32(55),"sc":int32(5),"el":int32(55),"src":"$PRAVDA_TMP_DIR/ZooProgram.cs","ec":int32(6) + } push int32(2) dupn push int8(14) @@ -412,22 +427,23 @@ translation: |- sput pop pop - @TransferPet_br82: + @TransferPet_br70: jump @TransferPet_lvc @TransferPet_lvc: pop pop pop pop - pop jump @stop @method_TransferZoo: meta method { "name":"TransferZoo",int32(1):int8(3),int32(0):int8(14),"returnTpe":int8(0) } - push null + meta source_mark { + "sl":int32(32),"sc":int32(9),"el":int32(32),"src":"$PRAVDA_TMP_DIR/ZooProgram.cs","ec":int32(72) + } push x5A6F6F546F4F776E6572 - push int32(4) + push int32(3) dupn push x call @stdlib_storage_get_default @@ -435,24 +451,26 @@ translation: |- eq push int8(3) cast - push int32(2) - swapn - pop - push int32(1) - dupn + meta source_mark { + "sl":int32(33),"sc":int32(13),"el":int32(33),"src":"$PRAVDA_TMP_DIR/ZooProgram.cs","ec":int32(34) + } push int8(9) cast not push int8(3) cast - push int32(1) + push int32(0) eq - jumpi @TransferZoo_br45 + not + jumpi @TransferZoo_br37 push x5A6F6F546F4F776E6572 - push int32(4) + push int32(3) dupn - push int32(6) + push int32(5) dupn + meta source_mark { + "sl":int32(35),"sc":int32(5),"el":int32(35),"src":"$PRAVDA_TMP_DIR/ZooProgram.cs","ec":int32(6) + } push int32(2) dupn push int8(14) @@ -463,13 +481,12 @@ translation: |- sput pop pop - @TransferZoo_br45: + @TransferZoo_br37: jump @TransferZoo_lvc @TransferZoo_lvc: pop pop pop - pop jump @stop @method_ctor: meta method { @@ -485,9 +502,33 @@ translation: |- push null push "init" sput + push int32(0) + push "p_ZooCnt" + sput + push int32(0) + push "p_PetId" + sput + meta source_mark { + "sl":int32(7),"sc":int32(5),"el":int32(7),"src":"$PRAVDA_TMP_DIR/ZooProgram.cs","ec":int32(72) + } + meta source_mark { + "sl":int32(8),"sc":int32(5),"el":int32(8),"src":"$PRAVDA_TMP_DIR/ZooProgram.cs","ec":int32(80) + } + meta source_mark { + "sl":int32(9),"sc":int32(5),"el":int32(9),"src":"$PRAVDA_TMP_DIR/ZooProgram.cs","ec":int32(78) + } + meta source_mark { + "sl":int32(10),"sc":int32(5),"el":int32(10),"src":"$PRAVDA_TMP_DIR/ZooProgram.cs","ec":int32(72) + } + meta source_mark { + "sl":int32(11),"sc":int32(5),"el":int32(11),"src":"$PRAVDA_TMP_DIR/ZooProgram.cs","ec":int32(28) + } push int32(1) push "p_ZooCnt" sput + meta source_mark { + "sl":int32(12),"sc":int32(5),"el":int32(12),"src":"$PRAVDA_TMP_DIR/ZooProgram.cs","ec":int32(27) + } push int32(1) push "p_PetId" sput @@ -498,29 +539,36 @@ translation: |- @func_GenerateSignature: push null push null - push null - push null + meta source_mark { + "sl":int32(16),"sc":int32(9),"el":int32(16),"src":"$PRAVDA_TMP_DIR/ZooProgram.cs","ec":int32(38) + } push int32(10) push int8(1) new_array - push int32(5) + push int32(3) swapn pop + meta source_mark { + "sl":int32(17),"sc":int32(14),"el":int32(17),"src":"$PRAVDA_TMP_DIR/ZooProgram.cs","ec":int32(23) + } push int32(0) - push int32(4) + push int32(2) swapn pop - jump @GenerateSignature_br43 - @GenerateSignature_br13: - push int32(4) + jump @GenerateSignature_br40 + @GenerateSignature_br12: + push int32(2) dupn - push int32(4) + push int32(2) dupn - push int32(7) + meta source_mark { + "sl":int32(18),"sc":int32(13),"el":int32(18),"src":"$PRAVDA_TMP_DIR/ZooProgram.cs","ec":int32(64) + } + push int32(5) dupn - push int32(6) + push int32(4) dupn - push int32(9) + push int32(7) dupn length swap @@ -533,37 +581,37 @@ translation: |- cast swap array_mut - push int32(3) + push int32(1) dupn push int32(1) + meta source_mark { + "sl":int32(17),"sc":int32(33),"el":int32(17),"src":"$PRAVDA_TMP_DIR/ZooProgram.cs","ec":int32(36) + } add - push int32(4) + push int32(2) swapn pop - @GenerateSignature_br43: - push int32(3) + @GenerateSignature_br40: + push int32(1) dupn + meta source_mark { + "sl":int32(17),"sc":int32(25),"el":int32(17),"src":"$PRAVDA_TMP_DIR/ZooProgram.cs","ec":int32(31) + } push int32(10) swap lt push int8(3) cast - push int32(3) - swapn - pop - push int32(2) - dupn - push int32(1) + push int32(0) eq - jumpi @GenerateSignature_br13 - push int32(4) - dupn - call @stdlib_array_to_bytes + not + jumpi @GenerateSignature_br12 push int32(2) - swapn - pop - push int32(1) dupn + meta source_mark { + "sl":int32(20),"sc":int32(9),"el":int32(20),"src":"$PRAVDA_TMP_DIR/ZooProgram.cs","ec":int32(32) + } + call @stdlib_array_to_bytes jump @GenerateSignature_lvc @GenerateSignature_lvc: swap @@ -572,10 +620,6 @@ translation: |- pop swap pop - swap - pop - swap - pop ret @stdlib_array_to_bytes: dup diff --git a/dotnet/src/test/resources/translation/arrays.trs b/dotnet/src/test/resources/translation/arrays.trs deleted file mode 100644 index 0a316318..00000000 --- a/dotnet/src/test/resources/translation/arrays.trs +++ /dev/null @@ -1,324 +0,0 @@ -exe: arrays.exe ---- -translation: |- - meta custom "CIL" - dup - push "ctor" - eq - jumpi @method_ctor - push "init" - sexist - jumpi @methods - push "Program was not initialized" - throw - @methods: - dup - push "WorkWithArrays" - eq - jumpi @method_WorkWithArrays - dup - push "WorkWithBytes" - eq - jumpi @method_WorkWithBytes - push "Wrong method name" - throw - @method_WorkWithArrays: - meta method { - "name":"WorkWithArrays","returnTpe":int8(0) - } - push null - push null - push null - push null - push null - push null - new int16[97, 98, 99] - push int32(7) - swapn - pop - new int32[1, 2, 3] - push int32(6) - swapn - pop - new number[1.0, 2.0, 3.0] - push int32(5) - swapn - pop - push int32(3) - push int8(11) - new_array - dup - push int32(0) - push "abc" - swap - array_mut - dup - push int32(1) - push "def" - swap - array_mut - dup - push int32(2) - push "rty" - swap - array_mut - push int32(4) - swapn - pop - new int32[4, 5, 6] - push int32(3) - swapn - pop - push int32(6) - dupn - push int32(1) - push int32(100) - swap - array_mut - push int32(5) - dupn - push int32(1) - push int32(4) - swap - array_mut - push int32(4) - dupn - push int32(1) - push number(4.0) - swap - array_mut - push int32(3) - dupn - push int32(1) - push "asdf" - swap - array_mut - push int32(2) - dupn - push int32(1) - push int32(7) - swap - array_mut - push int32(3) - dupn - length - push int8(3) - cast - push int32(2) - swapn - pop - jump @WorkWithArrays_lvc - @WorkWithArrays_lvc: - pop - pop - pop - pop - pop - pop - pop - jump @stop - @method_WorkWithBytes: - meta method { - "name":"WorkWithBytes","returnTpe":int8(0) - } - push null - push null - push null - push null - push null - push null - push null - push null - push null - push null - new int8[1, 2, 3] - push int32(11) - swapn - pop - new int8[4, 5, 6] - call @stdlib_array_to_bytes - push int32(10) - swapn - pop - new int8[7, 8, 9] - call @stdlib_array_to_bytes - push int32(9) - swapn - pop - push int32(10) - dupn - push int32(0) - array_get - push int32(8) - swapn - pop - push int32(10) - dupn - push int32(2) - array_get - push int32(7) - swapn - pop - push int32(9) - dupn - push int32(1) - array_get - push int32(6) - swapn - pop - push int32(8) - dupn - push int32(1) - array_get - push int32(5) - swapn - pop - push int32(9) - dupn - push int32(1) - push int32(2) - push int32(2) - dupn - add - swap - slice - push int32(4) - swapn - pop - push x6279746573 - push int32(10) - dupn - push int32(10) - dupn - push int32(2) - dupn - push int8(14) - cast - push int32(4) - dupn - concat - sput - pop - pop - push x6279746573 - new int8[8, 9, 10] - call @stdlib_array_to_bytes - push int8(14) - cast - swap - concat - sexist - push int8(3) - cast - push int32(2) - swapn - pop - push int32(1) - dupn - push int8(9) - cast - not - push int8(3) - cast - push int32(1) - eq - jumpi @WorkWithBytes_br192 - push x6279746573 - push int32(10) - dupn - new int8[7, 8, 9] - call @stdlib_array_to_bytes - push int32(2) - dupn - push int8(14) - cast - push int32(4) - dupn - concat - sput - pop - pop - @WorkWithBytes_br192: - push int32(10) - dupn - push int32(0) - push int32(2) - swap - array_mut - push int32(10) - dupn - push int32(1) - push int32(1) - swap - array_mut - push int32(9) - dupn - length - push int32(3) - swapn - pop - jump @WorkWithBytes_lvc - @WorkWithBytes_lvc: - pop - pop - pop - pop - pop - pop - pop - pop - pop - pop - pop - jump @stop - @method_ctor: - meta method { - "name":"ctor","returnTpe":int8(0) - } - push "init" - sexist - not - jumpi @ctor_ok - push "Program has been already initialized" - throw - @ctor_ok: - push null - push "init" - sput - jump @ctor_lvc - @ctor_lvc: - pop - jump @stop - @stdlib_array_to_bytes: - dup - length - push x - push int32(0) - @array_to_bytes_loop: - push int32(4) - dupn - push int32(2) - dupn - array_get - push int8(14) - cast - push int32(3) - dupn - concat - push int32(3) - swapn - pop - push int32(1) - add - dup - push int32(4) - dupn - gt - jumpi @array_to_bytes_loop - pop - swap - pop - swap - pop - ret - @stop: diff --git a/dotnet/src/test/resources/translation/compare.trs b/dotnet/src/test/resources/translation/compare.trs deleted file mode 100644 index 4447ca5b..00000000 --- a/dotnet/src/test/resources/translation/compare.trs +++ /dev/null @@ -1,1315 +0,0 @@ -exe: compare.exe ---- -translation: |- - meta custom "CIL" - dup - push "ctor" - eq - jumpi @method_ctor - push "init" - sexist - jumpi @methods - push "Program was not initialized" - throw - @methods: - dup - push "compare" - eq - jumpi @method_compare - push "Wrong method name" - throw - @method_compare: - meta method { - "name":"compare","returnTpe":int8(0) - } - push null - push null - push null - push null - push null - push null - push null - push null - push null - push null - push null - push null - push null - push null - push null - push null - push null - push null - push null - push null - push null - push null - push null - push null - push null - push null - push null - push null - push null - push null - push null - push null - push null - push null - push null - push null - push null - push int32(1) - push int32(38) - swapn - pop - push int32(2) - push int32(37) - swapn - pop - push int32(3) - push int32(36) - swapn - pop - push int32(4) - push int32(35) - swapn - pop - push int32(5) - push int8(4) - cast - push int32(34) - swapn - pop - push int32(6) - push int8(4) - cast - push int32(33) - swapn - pop - push int32(0) - push int32(32) - swapn - pop - push int32(37) - dupn - push int32(37) - dupn - eq - push int8(3) - cast - push int32(32) - swapn - pop - push int32(37) - dupn - push int8(4) - cast - push int32(36) - dupn - push int8(4) - cast - eq - push int8(3) - cast - push int32(32) - swapn - pop - push int32(35) - dupn - push int32(35) - dupn - eq - push int8(3) - cast - push int32(32) - swapn - pop - push int32(35) - dupn - push int8(4) - cast - push int32(34) - dupn - eq - push int8(3) - cast - push int32(32) - swapn - pop - push int32(33) - dupn - push int32(33) - dupn - eq - push int8(3) - cast - push int32(32) - swapn - pop - push int32(37) - dupn - push int32(37) - dupn - eq - push int8(3) - cast - push int32(31) - swapn - pop - push int32(30) - dupn - push int8(9) - cast - not - push int8(3) - cast - push int32(1) - eq - jumpi @compare_br68 - @compare_br68: - push int32(37) - dupn - push int8(4) - cast - push int32(36) - dupn - push int8(4) - cast - eq - push int8(3) - cast - push int32(30) - swapn - pop - push int32(29) - dupn - push int8(9) - cast - not - push int8(3) - cast - push int32(1) - eq - jumpi @compare_br82 - @compare_br82: - push int32(35) - dupn - push int32(35) - dupn - eq - push int8(3) - cast - push int32(29) - swapn - pop - push int32(28) - dupn - push int8(9) - cast - not - push int8(3) - cast - push int32(1) - eq - jumpi @compare_br94 - @compare_br94: - push int32(35) - dupn - push int8(4) - cast - push int32(34) - dupn - eq - push int8(3) - cast - push int32(28) - swapn - pop - push int32(27) - dupn - push int8(9) - cast - not - push int8(3) - cast - push int32(1) - eq - jumpi @compare_br108 - @compare_br108: - push int32(33) - dupn - push int32(33) - dupn - eq - push int8(3) - cast - push int32(27) - swapn - pop - push int32(26) - dupn - push int8(9) - cast - not - push int8(3) - cast - push int32(1) - eq - jumpi @compare_br122 - @compare_br122: - push int32(37) - dupn - push int32(37) - dupn - swap - lt - push int8(3) - cast - push int32(0) - eq - push int8(3) - cast - push int32(32) - swapn - pop - push int32(37) - dupn - push int8(4) - cast - push int32(36) - dupn - push int8(4) - cast - swap - lt - push int8(3) - cast - push int32(0) - eq - push int8(3) - cast - push int32(32) - swapn - pop - push int32(35) - dupn - push int32(35) - dupn - swap - lt - push int8(3) - cast - push int32(0) - eq - push int8(3) - cast - push int32(32) - swapn - pop - push int32(35) - dupn - push int8(4) - cast - push int32(34) - dupn - swap - lt - push int8(3) - cast - push int32(0) - eq - push int8(3) - cast - push int32(32) - swapn - pop - push int32(33) - dupn - push int32(33) - dupn - swap - lt - push int8(3) - cast - push int32(0) - eq - push int8(3) - cast - push int32(32) - swapn - pop - push int32(37) - dupn - push int32(37) - dupn - swap - lt - push int8(3) - cast - push int32(0) - eq - push int8(3) - cast - push int32(26) - swapn - pop - push int32(25) - dupn - push int8(9) - cast - not - push int8(3) - cast - push int32(1) - eq - jumpi @compare_br188 - @compare_br188: - push int32(37) - dupn - push int8(4) - cast - push int32(36) - dupn - push int8(4) - cast - swap - lt - push int8(3) - cast - push int32(0) - eq - push int8(3) - cast - push int32(25) - swapn - pop - push int32(24) - dupn - push int8(9) - cast - not - push int8(3) - cast - push int32(1) - eq - jumpi @compare_br205 - @compare_br205: - push int32(35) - dupn - push int32(35) - dupn - swap - lt - push int8(3) - cast - push int32(0) - eq - push int8(3) - cast - push int32(24) - swapn - pop - push int32(23) - dupn - push int8(9) - cast - not - push int8(3) - cast - push int32(1) - eq - jumpi @compare_br220 - @compare_br220: - push int32(35) - dupn - push int8(4) - cast - push int32(34) - dupn - swap - lt - push int8(3) - cast - push int32(0) - eq - push int8(3) - cast - push int32(23) - swapn - pop - push int32(22) - dupn - push int8(9) - cast - not - push int8(3) - cast - push int32(1) - eq - jumpi @compare_br237 - @compare_br237: - push int32(33) - dupn - push int32(33) - dupn - swap - lt - push int8(3) - cast - push int32(0) - eq - push int8(3) - cast - push int32(22) - swapn - pop - push int32(21) - dupn - push int8(9) - cast - not - push int8(3) - cast - push int32(1) - eq - jumpi @compare_br254 - @compare_br254: - push int32(37) - dupn - push int32(37) - dupn - swap - gt - push int8(3) - cast - push int32(0) - eq - push int8(3) - cast - push int32(32) - swapn - pop - push int32(37) - dupn - push int8(4) - cast - push int32(36) - dupn - push int8(4) - cast - swap - gt - push int8(3) - cast - push int32(0) - eq - push int8(3) - cast - push int32(32) - swapn - pop - push int32(35) - dupn - push int32(35) - dupn - swap - gt - push int8(3) - cast - push int32(0) - eq - push int8(3) - cast - push int32(32) - swapn - pop - push int32(35) - dupn - push int8(4) - cast - push int32(34) - dupn - swap - gt - push int8(3) - cast - push int32(0) - eq - push int8(3) - cast - push int32(32) - swapn - pop - push int32(33) - dupn - push int32(33) - dupn - swap - gt - push int8(3) - cast - push int32(0) - eq - push int8(3) - cast - push int32(32) - swapn - pop - push int32(37) - dupn - push int32(37) - dupn - swap - gt - push int8(3) - cast - push int32(0) - eq - push int8(3) - cast - push int32(21) - swapn - pop - push int32(20) - dupn - push int8(9) - cast - not - push int8(3) - cast - push int32(1) - eq - jumpi @compare_br320 - @compare_br320: - push int32(37) - dupn - push int8(4) - cast - push int32(36) - dupn - push int8(4) - cast - swap - gt - push int8(3) - cast - push int32(0) - eq - push int8(3) - cast - push int32(20) - swapn - pop - push int32(19) - dupn - push int8(9) - cast - not - push int8(3) - cast - push int32(1) - eq - jumpi @compare_br337 - @compare_br337: - push int32(35) - dupn - push int32(35) - dupn - swap - gt - push int8(3) - cast - push int32(0) - eq - push int8(3) - cast - push int32(19) - swapn - pop - push int32(18) - dupn - push int8(9) - cast - not - push int8(3) - cast - push int32(1) - eq - jumpi @compare_br352 - @compare_br352: - push int32(35) - dupn - push int8(4) - cast - push int32(34) - dupn - swap - gt - push int8(3) - cast - push int32(0) - eq - push int8(3) - cast - push int32(18) - swapn - pop - push int32(17) - dupn - push int8(9) - cast - not - push int8(3) - cast - push int32(1) - eq - jumpi @compare_br369 - @compare_br369: - push int32(33) - dupn - push int32(33) - dupn - swap - gt - push int8(3) - cast - push int32(0) - eq - push int8(3) - cast - push int32(17) - swapn - pop - push int32(16) - dupn - push int8(9) - cast - not - push int8(3) - cast - push int32(1) - eq - jumpi @compare_br386 - @compare_br386: - push int32(37) - dupn - push int32(37) - dupn - eq - push int8(3) - cast - push int32(0) - eq - push int8(3) - cast - push int32(32) - swapn - pop - push int32(37) - dupn - push int8(4) - cast - push int32(36) - dupn - push int8(4) - cast - eq - push int8(3) - cast - push int32(0) - eq - push int8(3) - cast - push int32(32) - swapn - pop - push int32(35) - dupn - push int32(35) - dupn - eq - push int8(3) - cast - push int32(0) - eq - push int8(3) - cast - push int32(32) - swapn - pop - push int32(35) - dupn - push int8(4) - cast - push int32(34) - dupn - eq - push int8(3) - cast - push int32(0) - eq - push int8(3) - cast - push int32(32) - swapn - pop - push int32(33) - dupn - push int32(33) - dupn - eq - push int8(3) - cast - push int32(0) - eq - push int8(3) - cast - push int32(32) - swapn - pop - push int32(37) - dupn - push int32(37) - dupn - eq - push int8(3) - cast - push int32(0) - eq - push int8(3) - cast - push int32(16) - swapn - pop - push int32(15) - dupn - push int8(9) - cast - not - push int8(3) - cast - push int32(1) - eq - jumpi @compare_br452 - @compare_br452: - push int32(37) - dupn - push int8(4) - cast - push int32(36) - dupn - push int8(4) - cast - eq - push int8(3) - cast - push int32(0) - eq - push int8(3) - cast - push int32(15) - swapn - pop - push int32(14) - dupn - push int8(9) - cast - not - push int8(3) - cast - push int32(1) - eq - jumpi @compare_br469 - @compare_br469: - push int32(35) - dupn - push int32(35) - dupn - eq - push int8(3) - cast - push int32(0) - eq - push int8(3) - cast - push int32(14) - swapn - pop - push int32(13) - dupn - push int8(9) - cast - not - push int8(3) - cast - push int32(1) - eq - jumpi @compare_br484 - @compare_br484: - push int32(35) - dupn - push int8(4) - cast - push int32(34) - dupn - eq - push int8(3) - cast - push int32(0) - eq - push int8(3) - cast - push int32(13) - swapn - pop - push int32(12) - dupn - push int8(9) - cast - not - push int8(3) - cast - push int32(1) - eq - jumpi @compare_br501 - @compare_br501: - push int32(33) - dupn - push int32(33) - dupn - eq - push int8(3) - cast - push int32(0) - eq - push int8(3) - cast - push int32(12) - swapn - pop - push int32(11) - dupn - push int8(9) - cast - not - push int8(3) - cast - push int32(1) - eq - jumpi @compare_br518 - @compare_br518: - push int32(37) - dupn - push int32(37) - dupn - swap - gt - push int8(3) - cast - push int32(32) - swapn - pop - push int32(37) - dupn - push int8(4) - cast - push int32(36) - dupn - push int8(4) - cast - swap - gt - push int8(3) - cast - push int32(32) - swapn - pop - push int32(35) - dupn - push int32(35) - dupn - swap - gt - push int8(3) - cast - push int32(32) - swapn - pop - push int32(35) - dupn - push int8(4) - cast - push int32(34) - dupn - swap - gt - push int8(3) - cast - push int32(32) - swapn - pop - push int32(33) - dupn - push int32(33) - dupn - swap - gt - push int8(3) - cast - push int32(32) - swapn - pop - push int32(37) - dupn - push int32(37) - dupn - swap - gt - push int8(3) - cast - push int32(11) - swapn - pop - push int32(10) - dupn - push int8(9) - cast - not - push int8(3) - cast - push int32(1) - eq - jumpi @compare_br566 - @compare_br566: - push int32(37) - dupn - push int8(4) - cast - push int32(36) - dupn - push int8(4) - cast - swap - gt - push int8(3) - cast - push int32(10) - swapn - pop - push int32(9) - dupn - push int8(9) - cast - not - push int8(3) - cast - push int32(1) - eq - jumpi @compare_br580 - @compare_br580: - push int32(35) - dupn - push int32(35) - dupn - swap - gt - push int8(3) - cast - push int32(9) - swapn - pop - push int32(8) - dupn - push int8(9) - cast - not - push int8(3) - cast - push int32(1) - eq - jumpi @compare_br592 - @compare_br592: - push int32(35) - dupn - push int8(4) - cast - push int32(34) - dupn - swap - gt - push int8(3) - cast - push int32(8) - swapn - pop - push int32(7) - dupn - push int8(9) - cast - not - push int8(3) - cast - push int32(1) - eq - jumpi @compare_br606 - @compare_br606: - push int32(33) - dupn - push int32(33) - dupn - swap - gt - push int8(3) - cast - push int32(7) - swapn - pop - push int32(6) - dupn - push int8(9) - cast - not - push int8(3) - cast - push int32(1) - eq - jumpi @compare_br620 - @compare_br620: - push int32(37) - dupn - push int32(37) - dupn - swap - lt - push int8(3) - cast - push int32(32) - swapn - pop - push int32(37) - dupn - push int8(4) - cast - push int32(36) - dupn - push int8(4) - cast - swap - lt - push int8(3) - cast - push int32(32) - swapn - pop - push int32(35) - dupn - push int32(35) - dupn - swap - lt - push int8(3) - cast - push int32(32) - swapn - pop - push int32(35) - dupn - push int8(4) - cast - push int32(34) - dupn - swap - lt - push int8(3) - cast - push int32(32) - swapn - pop - push int32(33) - dupn - push int32(33) - dupn - swap - lt - push int8(3) - cast - push int32(32) - swapn - pop - push int32(37) - dupn - push int32(37) - dupn - swap - lt - push int8(3) - cast - push int32(6) - swapn - pop - push int32(5) - dupn - push int8(9) - cast - not - push int8(3) - cast - push int32(1) - eq - jumpi @compare_br668 - @compare_br668: - push int32(37) - dupn - push int8(4) - cast - push int32(36) - dupn - push int8(4) - cast - swap - lt - push int8(3) - cast - push int32(5) - swapn - pop - push int32(4) - dupn - push int8(9) - cast - not - push int8(3) - cast - push int32(1) - eq - jumpi @compare_br682 - @compare_br682: - push int32(35) - dupn - push int32(35) - dupn - swap - lt - push int8(3) - cast - push int32(4) - swapn - pop - push int32(3) - dupn - push int8(9) - cast - not - push int8(3) - cast - push int32(1) - eq - jumpi @compare_br694 - @compare_br694: - push int32(35) - dupn - push int8(4) - cast - push int32(34) - dupn - swap - lt - push int8(3) - cast - push int32(3) - swapn - pop - push int32(2) - dupn - push int8(9) - cast - not - push int8(3) - cast - push int32(1) - eq - jumpi @compare_br708 - @compare_br708: - push int32(33) - dupn - push int32(33) - dupn - swap - lt - push int8(3) - cast - push int32(2) - swapn - pop - push int32(1) - dupn - push int8(9) - cast - not - push int8(3) - cast - push int32(1) - eq - jumpi @compare_br722 - @compare_br722: - jump @compare_lvc - @compare_lvc: - pop - pop - pop - pop - pop - pop - pop - pop - pop - pop - pop - pop - pop - pop - pop - pop - pop - pop - pop - pop - pop - pop - pop - pop - pop - pop - pop - pop - pop - pop - pop - pop - pop - pop - pop - pop - pop - pop - jump @stop - @method_ctor: - meta method { - "name":"ctor","returnTpe":int8(0) - } - push "init" - sexist - not - jumpi @ctor_ok - push "Program has been already initialized" - throw - @ctor_ok: - push null - push "init" - sput - jump @ctor_lvc - @ctor_lvc: - pop - jump @stop - @stop: diff --git a/dotnet/src/test/resources/translation/if.trs b/dotnet/src/test/resources/translation/if.trs deleted file mode 100644 index 8395cb27..00000000 --- a/dotnet/src/test/resources/translation/if.trs +++ /dev/null @@ -1,325 +0,0 @@ -exe: if.exe ---- -translation: |- - meta custom "CIL" - dup - push "ctor" - eq - jumpi @method_ctor - push "init" - sexist - jumpi @methods - push "Program was not initialized" - throw - @methods: - dup - push "ifs" - eq - jumpi @method_ifs - push "Wrong method name" - throw - @method_ctor: - meta method { - "name":"ctor","returnTpe":int8(0) - } - push "init" - sexist - not - jumpi @ctor_ok - push "Program has been already initialized" - throw - @ctor_ok: - push null - push "init" - sput - jump @ctor_lvc - @ctor_lvc: - pop - jump @stop - @method_ifs: - meta method { - "name":"ifs","returnTpe":int8(0) - } - push null - push null - push null - push null - push null - push null - push null - push null - push int32(10) - push int32(9) - swapn - pop - push int32(8) - dupn - push int32(1) - swap - lt - push int8(3) - cast - push int32(8) - swapn - pop - push int32(7) - dupn - push int8(9) - cast - not - push int8(3) - cast - push int32(1) - eq - jumpi @ifs_br16 - push int32(4) - push int32(9) - swapn - pop - @ifs_br16: - push int32(8) - dupn - push int32(5) - swap - gt - push int8(3) - cast - push int32(7) - swapn - pop - push int32(6) - dupn - push int8(9) - cast - not - push int8(3) - cast - push int32(1) - eq - jumpi @ifs_br38 - push int32(8) - dupn - push int32(6) - swap - gt - push int8(3) - cast - push int32(6) - swapn - pop - push int32(5) - dupn - push int8(9) - cast - not - push int8(3) - cast - push int32(1) - eq - jumpi @ifs_br37 - push int32(7) - push int32(9) - swapn - pop - @ifs_br37: - @ifs_br38: - push int32(8) - dupn - push int32(0) - swap - gt - push int8(3) - cast - push int32(5) - swapn - pop - push int32(4) - dupn - push int8(9) - cast - not - push int8(3) - cast - push int32(1) - eq - jumpi @ifs_br54 - push int32(4) - push int32(9) - swapn - pop - jump @ifs_br58 - @ifs_br54: - push int32(5) - push int32(9) - swapn - pop - @ifs_br58: - push int32(8) - dupn - push int32(2) - swap - gt - push int8(3) - cast - push int8(9) - cast - not - push int8(3) - cast - push int32(1) - eq - jumpi @ifs_br68 - push int32(8) - dupn - push int32(4) - swap - lt - push int8(3) - cast - jump @ifs_br69 - @ifs_br68: - push int32(0) - @ifs_br69: - push int32(4) - swapn - pop - push int32(3) - dupn - push int8(9) - cast - not - push int8(3) - cast - push int32(1) - eq - jumpi @ifs_br81 - push int32(6) - push int32(9) - swapn - pop - jump @ifs_br85 - @ifs_br81: - push int32(8) - push int32(9) - swapn - pop - @ifs_br85: - push int32(8) - dupn - push int32(7) - swap - gt - push int8(3) - cast - push int32(1) - eq - jumpi @ifs_br96 - push int32(8) - dupn - push int32(10) - swap - gt - push int8(3) - cast - jump @ifs_br97 - @ifs_br96: - push int32(1) - @ifs_br97: - push int32(3) - swapn - pop - push int32(2) - dupn - push int8(9) - cast - not - push int8(3) - cast - push int32(1) - eq - jumpi @ifs_br109 - push int32(1) - push int32(9) - swapn - pop - jump @ifs_br113 - @ifs_br109: - push int32(0) - push int32(9) - swapn - pop - @ifs_br113: - push int32(8) - dupn - push int32(1) - swap - gt - push int8(3) - cast - push int8(9) - cast - not - push int8(3) - cast - push int32(1) - eq - jumpi @ifs_br121 - push int32(8) - dupn - push int32(3) - swap - lt - push int8(3) - cast - push int32(1) - eq - jumpi @ifs_br128 - @ifs_br121: - push int32(8) - dupn - push int32(20) - swap - gt - push int8(3) - cast - jump @ifs_br129 - @ifs_br128: - push int32(1) - @ifs_br129: - push int32(2) - swapn - pop - push int32(1) - dupn - push int8(9) - cast - not - push int8(3) - cast - push int32(1) - eq - jumpi @ifs_br141 - push int32(2) - push int32(9) - swapn - pop - jump @ifs_br145 - @ifs_br141: - push int32(3) - push int32(9) - swapn - pop - @ifs_br145: - jump @ifs_lvc - @ifs_lvc: - pop - pop - pop - pop - pop - pop - pop - pop - pop - jump @stop - @stop: diff --git a/dotnet/src/test/resources/translation/inheritance.trs b/dotnet/src/test/resources/translation/inheritance.trs deleted file mode 100644 index cbf7e79c..00000000 --- a/dotnet/src/test/resources/translation/inheritance.trs +++ /dev/null @@ -1,261 +0,0 @@ -exe: inheritance.exe ---- -translation: |- - meta custom "CIL" - dup - push "ctor" - eq - jumpi @method_ctor - push "init" - sexist - jumpi @methods - push "Program was not initialized" - throw - @methods: - dup - push "Func" - eq - jumpi @method_Func - push "Wrong method name" - throw - @method_Func: - meta method { - "name":"Func","returnTpe":int8(3) - } - push null - push null - push null - push null - push null - push null - push int32(100) - new {} - call @vtable_A - push int32(2) - swapn - call @func_A.ctor_int32 - push int32(7) - swapn - pop - push int32(200) - new {} - call @vtable_B - push int32(2) - swapn - call @func_B.ctor_int32 - push int32(6) - swapn - pop - push int32(6) - dupn - push int32(1) - dupn - struct_get "Answer" - call - swap - pop - push int32(6) - dupn - push int32(1) - dupn - struct_get "Answer" - call - swap - pop - add - push int32(5) - swapn - pop - push int32(6) - dupn - push int32(1) - dupn - struct_get "AnswerPlus1" - call - swap - pop - push int32(4) - swapn - pop - push int32(5) - dupn - push int32(1) - dupn - struct_get "AnswerPlus1" - call - swap - pop - push int32(3) - swapn - pop - push int32(3) - dupn - push int32(3) - dupn - add - push int32(2) - swapn - pop - push int32(1) - dupn - jump @Func_lvc - @Func_lvc: - swap - pop - swap - pop - swap - pop - swap - pop - swap - pop - swap - pop - swap - pop - jump @stop - @method_ctor: - meta method { - "name":"ctor","returnTpe":int8(0) - } - push "init" - sexist - not - jumpi @ctor_ok - push "Program has been already initialized" - throw - @ctor_ok: - push null - push "init" - sput - jump @ctor_lvc - @ctor_lvc: - pop - jump @stop - @func_A.Answer: - push null - push int32(2) - dupn - struct_get "AVal" - push int32(42) - add - push int32(2) - swapn - pop - push int32(1) - dupn - jump @A.Answer_lvc - @A.Answer_lvc: - swap - pop - ret - @func_A.ctor_int32: - push int32(2) - dupn - push int32(2) - dupn - call @func_Parent.ctor_int32 - pop - push int32(2) - dupn - push int32(2) - dupn - struct_mut "AVal" - jump @A.ctor_int32_lvc - @A.ctor_int32_lvc: - pop - ret - @func_B.Answer: - push null - push int32(2) - dupn - struct_get "BVal" - push int32(43) - add - push int32(2) - swapn - pop - push int32(1) - dupn - jump @B.Answer_lvc - @B.Answer_lvc: - swap - pop - ret - @func_B.ctor_int32: - push int32(2) - dupn - push int32(2) - dupn - call @func_Parent.ctor_int32 - pop - push int32(2) - dupn - push int32(2) - dupn - struct_mut "BVal" - jump @B.ctor_int32_lvc - @B.ctor_int32_lvc: - pop - ret - @func_Parent.Answer: - push null - push int32(0) - push int32(2) - swapn - pop - push int32(1) - dupn - jump @Parent.Answer_lvc - @Parent.Answer_lvc: - swap - pop - ret - @func_Parent.AnswerPlus1: - push null - push int32(2) - dupn - push int32(1) - dupn - struct_get "Answer" - call - swap - pop - push int32(1) - add - push int32(2) - swapn - pop - push int32(1) - dupn - jump @Parent.AnswerPlus1_lvc - @Parent.AnswerPlus1_lvc: - swap - pop - ret - @func_Parent.ctor_int32: - push int32(2) - dupn - pop - jump @Parent.ctor_int32_lvc - @Parent.ctor_int32_lvc: - pop - ret - @vtable_A: - dup - push @func_A.Answer - struct_mut "Answer" - dup - push @func_Parent.AnswerPlus1 - struct_mut "AnswerPlus1" - ret - @vtable_B: - dup - push @func_B.Answer - struct_mut "Answer" - dup - push @func_Parent.AnswerPlus1 - struct_mut "AnswerPlus1" - ret - @stop: diff --git a/dotnet/src/test/resources/translation/loop.trs b/dotnet/src/test/resources/translation/loop.trs deleted file mode 100644 index 0034a3f8..00000000 --- a/dotnet/src/test/resources/translation/loop.trs +++ /dev/null @@ -1,120 +0,0 @@ -exe: loop.exe ---- -translation: |- - meta custom "CIL" - dup - push "ctor" - eq - jumpi @method_ctor - push "init" - sexist - jumpi @methods - push "Program was not initialized" - throw - @methods: - dup - push "loops" - eq - jumpi @method_loops - push "Wrong method name" - throw - @method_ctor: - meta method { - "name":"ctor","returnTpe":int8(0) - } - push "init" - sexist - not - jumpi @ctor_ok - push "Program has been already initialized" - throw - @ctor_ok: - push null - push "init" - sput - jump @ctor_lvc - @ctor_lvc: - pop - jump @stop - @method_loops: - meta method { - "name":"loops","returnTpe":int8(0) - } - push null - push null - push null - push null - push int32(0) - push int32(5) - swapn - pop - push int32(0) - push int32(4) - swapn - pop - jump @loops_br17 - @loops_br7: - push int32(4) - dupn - push int32(2) - add - push int32(5) - swapn - pop - push int32(3) - dupn - push int32(1) - add - push int32(4) - swapn - pop - @loops_br17: - push int32(3) - dupn - push int32(10) - swap - lt - push int8(3) - cast - push int32(3) - swapn - pop - push int32(2) - dupn - push int32(1) - eq - jumpi @loops_br7 - jump @loops_br34 - @loops_br28: - push int32(4) - dupn - push int32(2) - mul - push int32(5) - swapn - pop - @loops_br34: - push int32(4) - dupn - push int32(10000) - swap - lt - push int8(3) - cast - push int32(2) - swapn - pop - push int32(1) - dupn - push int32(1) - eq - jumpi @loops_br28 - jump @loops_lvc - @loops_lvc: - pop - pop - pop - pop - pop - jump @stop - @stop: diff --git a/dotnet/src/test/resources/translation/loop_nested.trs b/dotnet/src/test/resources/translation/loop_nested.trs deleted file mode 100644 index 0fd45646..00000000 --- a/dotnet/src/test/resources/translation/loop_nested.trs +++ /dev/null @@ -1,185 +0,0 @@ -exe: loop_nested.exe ---- -translation: |- - meta custom "CIL" - dup - push "ctor" - eq - jumpi @method_ctor - push "init" - sexist - jumpi @methods - push "Program was not initialized" - throw - @methods: - dup - push "loops" - eq - jumpi @method_loops - push "Wrong method name" - throw - @method_ctor: - meta method { - "name":"ctor","returnTpe":int8(0) - } - push "init" - sexist - not - jumpi @ctor_ok - push "Program has been already initialized" - throw - @ctor_ok: - push null - push "init" - sput - jump @ctor_lvc - @ctor_lvc: - pop - jump @stop - @method_loops: - meta method { - "name":"loops","returnTpe":int8(0) - } - push null - push null - push null - push null - push null - push null - push null - push int32(0) - push int32(8) - swapn - pop - push int32(0) - push int32(7) - swapn - pop - jump @loops_br58 - @loops_br7: - push int32(0) - push int32(6) - swapn - pop - jump @loops_br42 - @loops_br12: - push int32(7) - dupn - push int32(2) - swap - mod - push int32(0) - eq - push int8(3) - cast - push int32(5) - swapn - pop - push int32(4) - dupn - push int8(9) - cast - not - push int8(3) - cast - push int32(1) - eq - jumpi @loops_br37 - push int32(7) - dupn - push int32(7) - dupn - push int32(7) - dupn - add - push int32(1000000007) - swap - mod - add - push int32(8) - swapn - pop - @loops_br37: - push int32(5) - dupn - push int32(1) - add - push int32(6) - swapn - pop - @loops_br42: - push int32(5) - dupn - push int32(20) - swap - lt - push int8(3) - cast - push int32(4) - swapn - pop - push int32(3) - dupn - push int32(1) - eq - jumpi @loops_br12 - push int32(6) - dupn - push int32(1) - add - push int32(7) - swapn - pop - @loops_br58: - push int32(6) - dupn - push int32(10) - swap - lt - push int8(3) - cast - push int32(3) - swapn - pop - push int32(2) - dupn - push int32(1) - eq - jumpi @loops_br7 - jump @loops_br77 - @loops_br71: - push int32(7) - dupn - push int32(2) - mul - push int32(8) - swapn - pop - @loops_br77: - push int32(7) - dupn - push int32(10000) - swap - lt - push int8(3) - cast - push int32(2) - swapn - pop - push int32(1) - dupn - push int32(1) - eq - jumpi @loops_br71 - jump @loops_lvc - @loops_lvc: - pop - pop - pop - pop - pop - pop - pop - pop - jump @stop - @stop: diff --git a/dotnet/src/test/resources/translation/objects.trs b/dotnet/src/test/resources/translation/objects.trs deleted file mode 100644 index b271f972..00000000 --- a/dotnet/src/test/resources/translation/objects.trs +++ /dev/null @@ -1,163 +0,0 @@ -exe: objects.exe ---- -translation: |- - meta custom "CIL" - dup - push "ctor" - eq - jumpi @method_ctor - push "init" - sexist - jumpi @methods - push "Program was not initialized" - throw - @methods: - dup - push "Func" - eq - jumpi @method_Func - push "Wrong method name" - throw - @method_Func: - meta method { - "name":"Func","returnTpe":int8(3) - } - push null - push null - push null - push null - push int32(100) - new {} - call @vtable_A - push int32(2) - swapn - call @func_A.ctor_int32 - push int32(5) - swapn - pop - push int32(200) - new {} - call @vtable_B - push int32(2) - swapn - call @func_B.ctor_int32 - push int32(4) - swapn - pop - push int32(4) - dupn - call @func_A.AnswerA - swap - pop - push int32(4) - dupn - call @func_B.AnswerB - swap - pop - add - push int32(3) - swapn - pop - push int32(2) - dupn - push int32(2) - swapn - pop - push int32(1) - dupn - jump @Func_lvc - @Func_lvc: - swap - pop - swap - pop - swap - pop - swap - pop - swap - pop - jump @stop - @method_ctor: - meta method { - "name":"ctor","returnTpe":int8(0) - } - push "init" - sexist - not - jumpi @ctor_ok - push "Program has been already initialized" - throw - @ctor_ok: - push null - push "init" - sput - jump @ctor_lvc - @ctor_lvc: - pop - jump @stop - @func_A.AnswerA: - push null - push int32(2) - dupn - struct_get "AVal" - push int32(42) - add - push int32(2) - swapn - pop - push int32(1) - dupn - jump @A.AnswerA_lvc - @A.AnswerA_lvc: - swap - pop - ret - @func_A.ctor_int32: - push int32(2) - dupn - pop - push int32(2) - dupn - push int32(2) - dupn - struct_mut "AVal" - jump @A.ctor_int32_lvc - @A.ctor_int32_lvc: - pop - ret - @func_B.AnswerB: - push null - push int32(2) - dupn - struct_get "BVal" - push int32(43) - add - push int32(2) - swapn - pop - push int32(1) - dupn - jump @B.AnswerB_lvc - @B.AnswerB_lvc: - swap - pop - ret - @func_B.ctor_int32: - push int32(2) - dupn - pop - push int32(2) - dupn - push int32(2) - dupn - struct_mut "BVal" - jump @B.ctor_int32_lvc - @B.ctor_int32_lvc: - pop - ret - @vtable_A: - ret - @vtable_B: - ret - @stop: diff --git a/dotnet/src/test/resources/translation/smart_program.trs b/dotnet/src/test/resources/translation/smart_program.trs deleted file mode 100644 index 0dff46d4..00000000 --- a/dotnet/src/test/resources/translation/smart_program.trs +++ /dev/null @@ -1,195 +0,0 @@ -exe: smart_program.exe ---- -translation: |- - meta custom "CIL" - dup - push "ctor" - eq - jumpi @method_ctor - push "init" - sexist - jumpi @methods - push "Program was not initialized" - throw - @methods: - dup - push "balanceOf" - eq - jumpi @method_balanceOf - dup - push "transfer" - eq - jumpi @method_transfer - push "Wrong method name" - throw - @method_balanceOf: - meta method { - "name":"balanceOf",int32(0):int8(14),"returnTpe":int8(3) - } - push null - push x62616C616E636573 - push int32(4) - dupn - push int32(0) - call @stdlib_storage_get_default - push int32(2) - swapn - pop - push int32(1) - dupn - jump @balanceOf_lvc - @balanceOf_lvc: - swap - pop - swap - pop - swap - pop - jump @stop - @method_ctor: - meta method { - "name":"ctor","returnTpe":int8(0) - } - push "init" - sexist - not - jumpi @ctor_ok - push "Program has been already initialized" - throw - @ctor_ok: - push null - push "init" - sput - jump @ctor_lvc - @ctor_lvc: - pop - jump @stop - @method_transfer: - meta method { - "name":"transfer",int32(1):int8(3),int32(0):int8(14),"returnTpe":int8(0) - } - push null - push null - push int32(4) - dupn - push int32(0) - swap - gt - push int8(3) - cast - push int32(3) - swapn - pop - push int32(2) - dupn - push int8(9) - cast - not - push int8(3) - cast - push int32(1) - eq - jumpi @transfer_br104 - push x62616C616E636573 - from - push int32(0) - call @stdlib_storage_get_default - push int32(5) - dupn - swap - lt - push int8(3) - cast - push int32(0) - eq - push int8(3) - cast - push int32(2) - swapn - pop - push int32(1) - dupn - push int8(9) - cast - not - push int8(3) - cast - push int32(1) - eq - jumpi @transfer_br103 - push x62616C616E636573 - from - push x62616C616E636573 - from - push int32(0) - call @stdlib_storage_get_default - push int32(7) - dupn - push int32(-1) - mul - add - push int32(2) - dupn - push int8(14) - cast - push int32(4) - dupn - concat - sput - pop - pop - push x62616C616E636573 - push int32(6) - dupn - push x62616C616E636573 - push int32(8) - dupn - push int32(0) - call @stdlib_storage_get_default - push int32(7) - dupn - add - push int32(2) - dupn - push int8(14) - cast - push int32(4) - dupn - concat - sput - pop - pop - @transfer_br103: - @transfer_br104: - jump @transfer_lvc - @transfer_lvc: - pop - pop - pop - pop - pop - jump @stop - @stdlib_storage_get_default: - push int32(2) - dupn - push int8(14) - cast - push int32(4) - dupn - concat - sexist - jumpi @get_default_if - swap - pop - swap - pop - ret - @get_default_if: - pop - push int8(14) - cast - swap - concat - sget - ret - @stop: diff --git a/dotnet/src/test/resources/translation/smart_program_pdb.trs b/dotnet/src/test/resources/translation/smart_program_pdb.trs deleted file mode 100644 index 9428fc94..00000000 --- a/dotnet/src/test/resources/translation/smart_program_pdb.trs +++ /dev/null @@ -1,231 +0,0 @@ -exe: smart_program.exe smart_program.pdb ---- -translation: |- - meta custom "CIL" - dup - push "ctor" - eq - jumpi @method_ctor - push "init" - sexist - jumpi @methods - push "Program was not initialized" - throw - @methods: - dup - push "balanceOf" - eq - jumpi @method_balanceOf - dup - push "transfer" - eq - jumpi @method_transfer - push "Wrong method name" - throw - @method_balanceOf: - meta method { - "name":"balanceOf",int32(0):int8(14),"returnTpe":int8(3) - } - push null - meta source_mark { - "sl":int32(8),"sc":int32(44),"el":int32(8),"src":"/tmp/pravda/smart_program.cs","ec":int32(45) - } - meta source_mark { - "sl":int32(9),"sc":int32(9),"el":int32(9),"src":"/tmp/pravda/smart_program.cs","ec":int32(51) - } - push x62616C616E636573 - push int32(4) - dupn - push int32(0) - call @stdlib_storage_get_default - push int32(2) - swapn - pop - push int32(1) - dupn - meta source_mark { - "sl":int32(10),"sc":int32(5),"el":int32(10),"src":"/tmp/pravda/smart_program.cs","ec":int32(6) - } - jump @balanceOf_lvc - @balanceOf_lvc: - swap - pop - swap - pop - swap - pop - jump @stop - @method_ctor: - meta method { - "name":"ctor","returnTpe":int8(0) - } - push "init" - sexist - not - jumpi @ctor_ok - push "Program has been already initialized" - throw - @ctor_ok: - push null - push "init" - sput - meta source_mark { - "sl":int32(6),"sc":int32(5),"el":int32(6),"src":"/tmp/pravda/smart_program.cs","ec":int32(62) - } - jump @ctor_lvc - @ctor_lvc: - pop - jump @stop - @method_transfer: - meta method { - "name":"transfer",int32(1):int8(3),int32(0):int8(14),"returnTpe":int8(0) - } - push null - push null - meta source_mark { - "sl":int32(12),"sc":int32(48),"el":int32(12),"src":"/tmp/pravda/smart_program.cs","ec":int32(49) - } - meta source_mark { - "sl":int32(13),"sc":int32(9),"el":int32(13),"src":"/tmp/pravda/smart_program.cs","ec":int32(24) - } - push int32(4) - dupn - push int32(0) - swap - gt - push int8(3) - cast - push int32(3) - swapn - pop - push int32(2) - dupn - push int8(9) - cast - not - push int8(3) - cast - push int32(1) - eq - jumpi @transfer_br104 - meta source_mark { - "sl":int32(13),"sc":int32(25),"el":int32(13),"src":"/tmp/pravda/smart_program.cs","ec":int32(26) - } - meta source_mark { - "sl":int32(14),"sc":int32(13),"el":int32(14),"src":"/tmp/pravda/smart_program.cs","ec":int32(65) - } - push x62616C616E636573 - from - push int32(0) - call @stdlib_storage_get_default - push int32(5) - dupn - swap - lt - push int8(3) - cast - push int32(0) - eq - push int8(3) - cast - push int32(2) - swapn - pop - push int32(1) - dupn - push int8(9) - cast - not - push int8(3) - cast - push int32(1) - eq - jumpi @transfer_br103 - meta source_mark { - "sl":int32(14),"sc":int32(66),"el":int32(14),"src":"/tmp/pravda/smart_program.cs","ec":int32(67) - } - meta source_mark { - "sl":int32(15),"sc":int32(17),"el":int32(15),"src":"/tmp/pravda/smart_program.cs","ec":int32(93) - } - push x62616C616E636573 - from - push x62616C616E636573 - from - push int32(0) - call @stdlib_storage_get_default - push int32(7) - dupn - push int32(-1) - mul - add - push int32(2) - dupn - push int8(14) - cast - push int32(4) - dupn - concat - sput - pop - pop - meta source_mark { - "sl":int32(16),"sc":int32(17),"el":int32(16),"src":"/tmp/pravda/smart_program.cs","ec":int32(71) - } - push x62616C616E636573 - push int32(6) - dupn - push x62616C616E636573 - push int32(8) - dupn - push int32(0) - call @stdlib_storage_get_default - push int32(7) - dupn - add - push int32(2) - dupn - push int8(14) - cast - push int32(4) - dupn - concat - sput - pop - pop - @transfer_br103: - @transfer_br104: - meta source_mark { - "sl":int32(17),"sc":int32(13),"el":int32(17),"src":"/tmp/pravda/smart_program.cs","ec":int32(14) - } - jump @transfer_lvc - @transfer_lvc: - pop - pop - pop - pop - pop - jump @stop - @stdlib_storage_get_default: - push int32(2) - dupn - push int8(14) - cast - push int32(4) - dupn - concat - sexist - jumpi @get_default_if - swap - pop - swap - pop - ret - @get_default_if: - pop - push int8(14) - cast - swap - concat - sget - ret - @stop: diff --git a/dotnet/src/test/resources/translation/static_class.trs b/dotnet/src/test/resources/translation/static_class.trs deleted file mode 100644 index a7ebeca9..00000000 --- a/dotnet/src/test/resources/translation/static_class.trs +++ /dev/null @@ -1,400 +0,0 @@ -exe: static_class.exe ---- -translation: |- - meta custom "CIL" - dup - push "ctor" - eq - jumpi @method_ctor - push "init" - sexist - jumpi @methods - push "Program was not initialized" - throw - @methods: - dup - push "ToHex" - eq - jumpi @method_ToHex - push "Wrong method name" - throw - @method_ToHex: - meta method { - "name":"ToHex",int32(0):int8(14),"returnTpe":int8(11) - } - push int32(2) - dupn - call @func_Expload.Pravda.StringUtils.BytesToHex_Expload.Pravda.Bytes - jump @ToHex_lvc - @ToHex_lvc: - swap - pop - swap - pop - jump @stop - @method_ctor: - meta method { - "name":"ctor","returnTpe":int8(0) - } - push "init" - sexist - not - jumpi @ctor_ok - push "Program has been already initialized" - throw - @ctor_ok: - push null - push "init" - sput - jump @ctor_lvc - @ctor_lvc: - pop - jump @stop - @func_Expload.Pravda.StringUtils.ByteToHex_uint8: - push int32(2) - dupn - push int32(16) - swap - div - call @func_Expload.Pravda.StringUtils.HexPart_int32 - push int32(3) - dupn - push int32(16) - swap - mod - call @func_Expload.Pravda.StringUtils.HexPart_int32 - swap - concat - jump @Expload.Pravda.StringUtils.ByteToHex_uint8_lvc - @Expload.Pravda.StringUtils.ByteToHex_uint8_lvc: - swap - pop - ret - @func_Expload.Pravda.StringUtils.BytesToHex_Expload.Pravda.Bytes: - push null - push null - push "" - push int32(3) - swapn - pop - push int32(0) - push int32(2) - swapn - pop - jump @Expload.Pravda.StringUtils.BytesToHex_Expload.Pravda.Bytes_br33 - @Expload.Pravda.StringUtils.BytesToHex_Expload.Pravda.Bytes_br10: - push int32(2) - dupn - push int32(5) - dupn - push int32(3) - dupn - array_get - call @func_Expload.Pravda.StringUtils.ByteToHex_uint8 - swap - concat - push int32(3) - swapn - pop - push int32(1) - dupn - push int32(1) - add - push int32(2) - swapn - pop - @Expload.Pravda.StringUtils.BytesToHex_Expload.Pravda.Bytes_br33: - push int32(1) - dupn - push int32(5) - dupn - length - swap - lt - push int8(3) - cast - push int32(1) - eq - jumpi @Expload.Pravda.StringUtils.BytesToHex_Expload.Pravda.Bytes_br10 - push int32(2) - dupn - jump @Expload.Pravda.StringUtils.BytesToHex_Expload.Pravda.Bytes_lvc - @Expload.Pravda.StringUtils.BytesToHex_Expload.Pravda.Bytes_lvc: - swap - pop - swap - pop - swap - pop - ret - @func_Expload.Pravda.StringUtils.HexPart_int32: - push int32(2) - dupn - push int32(1) - eq - jumpi @Expload.Pravda.StringUtils.HexPart_int32_br9 - push "0" - jump @Expload.Pravda.StringUtils.HexPart_int32_lvc - @Expload.Pravda.StringUtils.HexPart_int32_br9: - push int32(2) - dupn - push int32(1) - eq - push int8(3) - cast - push int8(9) - cast - not - push int8(3) - cast - push int32(1) - eq - jumpi @Expload.Pravda.StringUtils.HexPart_int32_br19 - push "1" - jump @Expload.Pravda.StringUtils.HexPart_int32_lvc - @Expload.Pravda.StringUtils.HexPart_int32_br19: - push int32(2) - dupn - push int32(2) - eq - push int8(3) - cast - push int8(9) - cast - not - push int8(3) - cast - push int32(1) - eq - jumpi @Expload.Pravda.StringUtils.HexPart_int32_br29 - push "2" - jump @Expload.Pravda.StringUtils.HexPart_int32_lvc - @Expload.Pravda.StringUtils.HexPart_int32_br29: - push int32(2) - dupn - push int32(3) - eq - push int8(3) - cast - push int8(9) - cast - not - push int8(3) - cast - push int32(1) - eq - jumpi @Expload.Pravda.StringUtils.HexPart_int32_br39 - push "3" - jump @Expload.Pravda.StringUtils.HexPart_int32_lvc - @Expload.Pravda.StringUtils.HexPart_int32_br39: - push int32(2) - dupn - push int32(4) - eq - push int8(3) - cast - push int8(9) - cast - not - push int8(3) - cast - push int32(1) - eq - jumpi @Expload.Pravda.StringUtils.HexPart_int32_br49 - push "4" - jump @Expload.Pravda.StringUtils.HexPart_int32_lvc - @Expload.Pravda.StringUtils.HexPart_int32_br49: - push int32(2) - dupn - push int32(5) - eq - push int8(3) - cast - push int8(9) - cast - not - push int8(3) - cast - push int32(1) - eq - jumpi @Expload.Pravda.StringUtils.HexPart_int32_br59 - push "5" - jump @Expload.Pravda.StringUtils.HexPart_int32_lvc - @Expload.Pravda.StringUtils.HexPart_int32_br59: - push int32(2) - dupn - push int32(6) - eq - push int8(3) - cast - push int8(9) - cast - not - push int8(3) - cast - push int32(1) - eq - jumpi @Expload.Pravda.StringUtils.HexPart_int32_br69 - push "6" - jump @Expload.Pravda.StringUtils.HexPart_int32_lvc - @Expload.Pravda.StringUtils.HexPart_int32_br69: - push int32(2) - dupn - push int32(7) - eq - push int8(3) - cast - push int8(9) - cast - not - push int8(3) - cast - push int32(1) - eq - jumpi @Expload.Pravda.StringUtils.HexPart_int32_br79 - push "7" - jump @Expload.Pravda.StringUtils.HexPart_int32_lvc - @Expload.Pravda.StringUtils.HexPart_int32_br79: - push int32(2) - dupn - push int32(8) - eq - push int8(3) - cast - push int8(9) - cast - not - push int8(3) - cast - push int32(1) - eq - jumpi @Expload.Pravda.StringUtils.HexPart_int32_br89 - push "8" - jump @Expload.Pravda.StringUtils.HexPart_int32_lvc - @Expload.Pravda.StringUtils.HexPart_int32_br89: - push int32(2) - dupn - push int32(9) - eq - push int8(3) - cast - push int8(9) - cast - not - push int8(3) - cast - push int32(1) - eq - jumpi @Expload.Pravda.StringUtils.HexPart_int32_br100 - push "9" - jump @Expload.Pravda.StringUtils.HexPart_int32_lvc - @Expload.Pravda.StringUtils.HexPart_int32_br100: - push int32(2) - dupn - push int32(10) - eq - push int8(3) - cast - push int8(9) - cast - not - push int8(3) - cast - push int32(1) - eq - jumpi @Expload.Pravda.StringUtils.HexPart_int32_br111 - push "A" - jump @Expload.Pravda.StringUtils.HexPart_int32_lvc - @Expload.Pravda.StringUtils.HexPart_int32_br111: - push int32(2) - dupn - push int32(11) - eq - push int8(3) - cast - push int8(9) - cast - not - push int8(3) - cast - push int32(1) - eq - jumpi @Expload.Pravda.StringUtils.HexPart_int32_br122 - push "B" - jump @Expload.Pravda.StringUtils.HexPart_int32_lvc - @Expload.Pravda.StringUtils.HexPart_int32_br122: - push int32(2) - dupn - push int32(12) - eq - push int8(3) - cast - push int8(9) - cast - not - push int8(3) - cast - push int32(1) - eq - jumpi @Expload.Pravda.StringUtils.HexPart_int32_br133 - push "C" - jump @Expload.Pravda.StringUtils.HexPart_int32_lvc - @Expload.Pravda.StringUtils.HexPart_int32_br133: - push int32(2) - dupn - push int32(13) - eq - push int8(3) - cast - push int8(9) - cast - not - push int8(3) - cast - push int32(1) - eq - jumpi @Expload.Pravda.StringUtils.HexPart_int32_br144 - push "D" - jump @Expload.Pravda.StringUtils.HexPart_int32_lvc - @Expload.Pravda.StringUtils.HexPart_int32_br144: - push int32(2) - dupn - push int32(14) - eq - push int8(3) - cast - push int8(9) - cast - not - push int8(3) - cast - push int32(1) - eq - jumpi @Expload.Pravda.StringUtils.HexPart_int32_br155 - push "E" - jump @Expload.Pravda.StringUtils.HexPart_int32_lvc - @Expload.Pravda.StringUtils.HexPart_int32_br155: - push int32(2) - dupn - push int32(15) - eq - push int8(3) - cast - push int8(9) - cast - not - push int8(3) - cast - push int32(1) - eq - jumpi @Expload.Pravda.StringUtils.HexPart_int32_br166 - push "F" - jump @Expload.Pravda.StringUtils.HexPart_int32_lvc - @Expload.Pravda.StringUtils.HexPart_int32_br166: - push "" - jump @Expload.Pravda.StringUtils.HexPart_int32_lvc - @Expload.Pravda.StringUtils.HexPart_int32_lvc: - swap - pop - ret - @stop: diff --git a/dotnet/src/test/resources/translation/string.trs b/dotnet/src/test/resources/translation/string.trs deleted file mode 100644 index 4d0f046b..00000000 --- a/dotnet/src/test/resources/translation/string.trs +++ /dev/null @@ -1,161 +0,0 @@ -exe: strings.exe ---- -translation: |- - meta custom "CIL" - dup - push "ctor" - eq - jumpi @method_ctor - push "init" - sexist - jumpi @methods - push "Program was not initialized" - throw - @methods: - dup - push "distributeSalary" - eq - jumpi @method_distributeSalary - push "Wrong method name" - throw - @method_ctor: - meta method { - "name":"ctor","returnTpe":int8(0) - } - push "init" - sexist - not - jumpi @ctor_ok - push "Program has been already initialized" - throw - @ctor_ok: - push null - push "init" - sput - jump @ctor_lvc - @ctor_lvc: - pop - jump @stop - @method_distributeSalary: - meta method { - "name":"distributeSalary","returnTpe":int8(0) - } - push null - push null - push null - push null - push null - push null - push null - push null - push "zauser1" - push int32(9) - swapn - pop - push "us" - push int32(8) - swapn - pop - push "er2" - push int32(7) - swapn - pop - push int32(7) - dupn - push int32(7) - dupn - swap - concat - push int32(6) - swapn - pop - push x737472696E6773 - push int32(6) - dupn - push int32(10) - dupn - push int32(2) - dupn - push int8(14) - cast - push int32(4) - dupn - concat - sput - pop - pop - push x737472696E6773 - push "user1" - push int8(14) - cast - swap - concat - sexist - push int8(3) - cast - push int32(2) - swapn - pop - push int32(1) - dupn - push int8(9) - cast - not - push int8(3) - cast - push int32(1) - eq - jumpi @distributeSalary_br87 - push x737472696E6773 - push "user2" - push "" - push int32(2) - dupn - push int8(14) - cast - push int32(4) - dupn - concat - sput - pop - pop - @distributeSalary_br87: - push int32(8) - dupn - push int32(0) - array_get - push int32(5) - swapn - pop - push int32(5) - dupn - push int32(3) - array_get - push int32(4) - swapn - pop - push int32(5) - dupn - push int32(1) - push int32(2) - push int32(2) - dupn - add - swap - slice - push int32(3) - swapn - pop - jump @distributeSalary_lvc - @distributeSalary_lvc: - pop - pop - pop - pop - pop - pop - pop - pop - pop - jump @stop - @stop: diff --git a/dotnet/src/test/resources/translation/system.trs b/dotnet/src/test/resources/translation/system.trs deleted file mode 100644 index 1e905ae8..00000000 --- a/dotnet/src/test/resources/translation/system.trs +++ /dev/null @@ -1,66 +0,0 @@ -exe: system.exe ---- -translation: |- - meta custom "CIL" - dup - push "ctor" - eq - jumpi @method_ctor - push "init" - sexist - jumpi @methods - push "Program was not initialized" - throw - @methods: - dup - push "system" - eq - jumpi @method_system - push "Wrong method name" - throw - @method_ctor: - meta method { - "name":"ctor","returnTpe":int8(0) - } - push "init" - sexist - not - jumpi @ctor_ok - push "Program has been already initialized" - throw - @ctor_ok: - push null - push "init" - sput - jump @ctor_lvc - @ctor_lvc: - pop - jump @stop - @method_system: - meta method { - "name":"system","returnTpe":int8(0) - } - push null - push null - push null - push x - balance - push int32(4) - swapn - pop - push x0000000000000000000000000000000000000000000000000000000000000000 - push int32(3) - swapn - pop - paddr - push int32(2) - swapn - pop - jump @system_lvc - @system_lvc: - pop - pop - pop - pop - jump @stop - @stop: diff --git a/dotnet/src/test/scala/pravda/dotnet/DotnetCompilation.scala b/dotnet/src/test/scala/pravda/dotnet/DotnetCompilation.scala new file mode 100644 index 00000000..18167bf7 --- /dev/null +++ b/dotnet/src/test/scala/pravda/dotnet/DotnetCompilation.scala @@ -0,0 +1,140 @@ +package pravda.dotnet +import java.nio.file._ + +import cats.implicits._ +import org.apache.commons.io.FileUtils +import pravda.dotnet.parser.FileParser +import pravda.dotnet.parser.FileParser.{ParsedDotnetFile, ParsedPdb, ParsedPe} + +import scala.util.Random + +final case class DotnetCompilationStep(target: String, sources: Seq[String], optimize: Boolean = false) + +final case class DotnetCompilation(steps: Seq[DotnetCompilationStep], `main-class`: Option[String] = None) + +object DotnetCompilation { + + object dsl { + + def steps(pairs: (String, Seq[String])*): DotnetCompilation = + DotnetCompilation(pairs.map { case (target, sources) => DotnetCompilationStep(target, sources, optimize = true) }) + + implicit class DotnetCompilationOps(dc: DotnetCompilation) { + def withMainClass(mainClass: String): DotnetCompilation = dc.copy(`main-class` = Some(mainClass)) + + def run: Either[String, List[ParsedDotnetFile]] = DotnetCompilation.run(dc) + } + } + + val pravdaDir = Paths.get(System.getProperty("java.io.tmpdir"), "pravda") + private def randomTempFolder: Path = pravdaDir.resolve(Random.alphanumeric.take(20).mkString) + private def readFileBytes(p: Path) = Files.readAllBytes(p) + + private def parsePeFile(p: Path): Either[String, ParsedPe] = + FileParser.parsePe(readFileBytes(p)) + + private def parsePdbFile(p: Path): Either[String, ParsedPdb] = + FileParser.parsePdb(readFileBytes(p)) + + def run(compilation: DotnetCompilation): Either[String, List[ParsedDotnetFile]] = DotnetCompilation.synchronized { + val tempFolder = randomTempFolder + Files.createDirectories(tempFolder) + + val (_, filesE) = + compilation.steps.foldLeft((Vector.empty[String], Vector.empty[Either[String, ParsedDotnetFile]])) { + case ((labels, res), DotnetCompilationStep(target, sources, optimize)) => + if (!target.endsWith(".dll") && !target.endsWith(".exe")) { + (labels, res :+ Left(s"Unknown extension of target file: $target")) + } else { + sources.find(s => !s.endsWith(".dll") && !s.endsWith(".cs")) match { + case None => + def restorePath(s: String): Path = { + if (labels.contains(s)) { + tempFolder.resolve(s) + } else { + val path = Paths.get(s) + val copyPath = tempFolder.resolve(path.getFileName) + // The standard Java 7 copy method fails with the strange error "NoSuchFileException" + // So we replaced that method to another implementation from Apache Commons IO + FileUtils.copyFile(path.toFile, copyPath.toFile) + copyPath + } + } + + val dlls = sources.filter(_.endsWith(".dll")).map(s => restorePath(s)) + val css = sources.filter(_.endsWith(".cs")).map(s => restorePath(s)) + val targetP = tempFolder.resolve(target) + val pdbP = tempFolder.resolve(target.dropRight(4) + ".pdb") + + val isChanged = + if (Files.exists(targetP)) { + val allTimes = dlls.map(p => Files.getLastModifiedTime(p)) ++ css.map(p => + Files.getLastModifiedTime(p)) + allTimes.map(_.toMillis).max > Files.getLastModifiedTime(targetP).toMillis + } else { + true + } + + val commandE = if (isChanged) { + val command = + s""" + |csc ${css.map(_.toAbsolutePath.toString).mkString(" ")} + |-out:${targetP.toAbsolutePath.toString} + |${dlls.map(dll => s"-reference:${dll.toAbsolutePath.toString}").mkString("\n")} + |-debug:portable + |-pdb:${pdbP.toAbsolutePath.toString} + |${if (target.endsWith(".dll")) "-target:library" else ""} + |${if (optimize) "-optimize+" else ""} + """.stripMargin.trim.replace("\n\n", "\n") + + def errorWithCommand(err: String) = + s""" + |command: + |$command + |error: + |$err + """.stripMargin + + val stdoutS = StringBuilder.newBuilder + val stderrS = StringBuilder.newBuilder + + { + import scala.sys.process._ + command.!(new ProcessLogger { + override def out(s: => String): Unit = { + stdoutS ++= s + stdoutS += '\n' + } + override def err(s: => String): Unit = { + stderrS ++= s + stderrS += '\n' + } + override def buffer[T](f: => T): T = f + }) + } + val msgLines = stdoutS.mkString.lines.toList ++ stderrS.mkString.lines.toList + val errorLines = msgLines.filter(_.contains("error")) + if (errorLines.isEmpty) { + Right(()) + } else { + Left(errorWithCommand(errorLines.mkString("\n"))) + } + } else { + Right(()) + } + + val parsed = for { + _ <- commandE + pe <- parsePeFile(targetP) + pdb <- parsePdbFile(pdbP) + } yield ParsedDotnetFile(pe, Some(pdb)) + + (labels :+ target, res :+ parsed) + case Some(s) => (labels, res :+ Left(s"Unknown extension of source file: $s")) + } + } + } + + filesE.toList.sequence + } +} diff --git a/dotnet/src/test/scala/pravda/dotnet/ParserSuite.scala b/dotnet/src/test/scala/pravda/dotnet/ParserSuite.scala index a6739da6..e78d9ebd 100644 --- a/dotnet/src/test/scala/pravda/dotnet/ParserSuite.scala +++ b/dotnet/src/test/scala/pravda/dotnet/ParserSuite.scala @@ -5,7 +5,7 @@ import java.io.File import pravda.plaintest._ object ParserSuiteData { - final case class Input(exe: String) + final case class Input(`dotnet-compilation`: DotnetCompilation) final case class Output(methods: String, signatures: String) } @@ -19,8 +19,12 @@ object ParserSuite extends Plaintest[Input, Output] { def produce(input: Input): Either[String, Output] = for { - pe <- parsePeFile(input.exe) + files <- DotnetCompilation.run(input.`dotnet-compilation`) + clearedFiles = clearPathsInPdb(files) + last = clearedFiles.last } yield - Output(pprint.apply(pe.methods, height = Int.MaxValue).plainText, - pprint.apply(pe.signatures.toList.sortBy(_._1), height = Int.MaxValue).plainText) + Output( + pprint.apply(last.parsedPe.methods, height = Int.MaxValue).plainText, + pprint.apply(last.parsedPe.signatures.toList.sortBy(_._1), height = Int.MaxValue).plainText + ) } diff --git a/dotnet/src/test/scala/pravda/dotnet/TranslationErrorTests.scala b/dotnet/src/test/scala/pravda/dotnet/TranslationErrorTests.scala index f944dbc1..8cecf952 100644 --- a/dotnet/src/test/scala/pravda/dotnet/TranslationErrorTests.scala +++ b/dotnet/src/test/scala/pravda/dotnet/TranslationErrorTests.scala @@ -1,18 +1,39 @@ package pravda.dotnet +import pravda.dotnet.DotnetCompilation.dsl._ import pravda.dotnet.translation.Translator import utest._ object TranslationErrorTests extends TestSuite { val tests = Tests { - 'error - { - val Right(pe) = parsePeFile("error.exe") - val Right(pdb) = parsePdbFile("error.pdb") + 'Error - { + val Right(files) = + steps( + "Pravda.dll" -> Seq("PravdaDotNet/Pravda.cs"), + "Error.exe" -> Seq("Pravda.dll", "dotnet-tests/resources/Error.cs") + ).run + + val pe = files.last.parsedPe + val pdb = files.last.parsedPdb.get Translator.translateAsm(pe, Some(pdb)).left.get.mkString ==> """|Call(MemberRefData(TypeRefData(6,Console,System),WriteLine,16)) is not supported - | error.cs:8,9-8,74""".stripMargin + | Error.cs:9,9-9,74""".stripMargin + } + + 'PublicMapping - { + val Right(files) = + steps( + "Pravda.dll" -> Seq("PravdaDotNet/Pravda.cs"), + "PublicMapping.exe" -> Seq("Pravda.dll", "dotnet-tests/resources/PublicMapping.cs") + ).run + + val pe = files.last.parsedPe + val pdb = files.last.parsedPdb.get + + Translator.translateAsm(pe, Some(pdb)).left.get.mkString ==> + "All [Program] fields must be private: SomeField in PublicMapping is not private" } } } diff --git a/dotnet/src/test/scala/pravda/dotnet/TranslationSuite.scala b/dotnet/src/test/scala/pravda/dotnet/TranslationSuite.scala index 2f250d81..bfbe78bc 100644 --- a/dotnet/src/test/scala/pravda/dotnet/TranslationSuite.scala +++ b/dotnet/src/test/scala/pravda/dotnet/TranslationSuite.scala @@ -2,17 +2,13 @@ package pravda.dotnet import java.io.File -import pravda.plaintest.Plaintest -import TranslationSuiteData._ +import pravda.dotnet.TranslationSuiteData._ import pravda.dotnet.translation.Translator +import pravda.plaintest.Plaintest import pravda.vm.asm.PravdaAssembler -import cats.instances.list._ -import cats.instances.either._ -import cats.syntax.traverse._ -import pravda.dotnet.parser.FileParser.ParsedDotnetFile object TranslationSuiteData { - final case class Input(exe: String) + final case class Input(`dotnet-compilation`: DotnetCompilation) final case class Output(translation: String) } @@ -23,34 +19,13 @@ object TranslationSuite extends Plaintest[Input, Output] { override lazy val allowOverwrite = true def produce(input: Input): Either[String, Output] = { - val lines = input.exe.lines.toList - val parts = lines.head.split("\\s+").toList - val mainClass = lines.tail.headOption - val filesE: Either[String, List[ParsedDotnetFile]] = parts - .groupBy(_.dropRight(4)) - .map { - case (prefix, files) => - val exeO = files.find(_ == s"$prefix.exe") - val dllO = files.find(_ == s"$prefix.dll") - val pdbO = files.find(_ == s"$prefix.pdb") - - (exeO, dllO) match { - case (Some(exe), Some(dll)) => - Left(s".dll and .exe files have the same name: $exe, $dll") - case (None, None) => - Left(s".dll or .exe is not specified: $prefix") - case (Some(exe), None) => - parseDotnetFile(exe, pdbO) - case (None, Some(dll)) => - parseDotnetFile(dll, pdbO) - } - } - .toList - .sequence - for { - files <- filesE - asm <- Translator.translateAsm(files, mainClass).left.map(_.mkString) - } yield Output(PravdaAssembler.render(asm)) + files <- DotnetCompilation.run(input.`dotnet-compilation`) + clearedFiles = clearPathsInPdb(files) + asm <- Translator.translateAsm(clearedFiles, input.`dotnet-compilation`.`main-class`).left.map(_.mkString) + } yield { + val rawAsm = PravdaAssembler.render(asm) + Output(rawAsm) + } } } diff --git a/dotnet/src/test/scala/pravda/dotnet/package.scala b/dotnet/src/test/scala/pravda/dotnet/package.scala index 28450b54..2704ff38 100644 --- a/dotnet/src/test/scala/pravda/dotnet/package.scala +++ b/dotnet/src/test/scala/pravda/dotnet/package.scala @@ -1,25 +1,25 @@ package pravda - -import java.nio.file.{Files, Paths} - import pravda.dotnet.parser.FileParser -import pravda.dotnet.parser.FileParser.{ParsedDotnetFile, ParsedPdb, ParsedPe} package object dotnet { - private def readResourceBytes(filename: String) = - Files.readAllBytes(Paths.get(s"dotnet-tests/resources/$filename")) + def clearPathsInPdb(files: List[FileParser.ParsedDotnetFile]): List[FileParser.ParsedDotnetFile] = { + def clear(s: String): String = "$PRAVDA_TMP_DIR/" + s"${s.split("/").last}" - def parsePeFile(file: String): Either[String, ParsedPe] = { - val fileBytes = readResourceBytes(file) - FileParser.parsePe(fileBytes) - } + // Drop the first Pravda.cs file + files.drop(1).map { df => + val clearedPdb = df.parsedPdb.map { pdb => + pdb.copy( + tablesData = pdb.tablesData.copy( + methodDebugInformationTable = + pdb.tablesData.methodDebugInformationTable.map(d => d.copy(document = d.document.map(clear))), + documentTable = pdb.tablesData.documentTable.map(d => d.copy(path = clear(d.path))) + ) + ) + } - def parsePdbFile(file: String): Either[String, ParsedPdb] = { - val fileBytes = readResourceBytes(file) - FileParser.parsePdb(fileBytes) + df.copy(parsedPdb = clearedPdb) + } } - def parseDotnetFile(exeFile: String, pdbFile: Option[String]): Either[String, ParsedDotnetFile] = - FileParser.parseDotnetFile(readResourceBytes(exeFile), pdbFile.map(readResourceBytes)) } diff --git a/dotnet/src/test/scala/pravda/dotnet/parser/InheritanceTests.scala b/dotnet/src/test/scala/pravda/dotnet/parser/InheritanceTests.scala index a9e5c6ff..54bde69b 100644 --- a/dotnet/src/test/scala/pravda/dotnet/parser/InheritanceTests.scala +++ b/dotnet/src/test/scala/pravda/dotnet/parser/InheritanceTests.scala @@ -3,14 +3,21 @@ package pravda.dotnet package parser import pravda.common.TestUtils +import pravda.dotnet.DotnetCompilation.dsl._ import pravda.dotnet.data.TablesData._ import utest._ object InheritanceTests extends TestSuite { val tests = Tests { - 'inheritanceParse - { - val Right(pe) = parsePeFile("inheritance.exe") + 'Inheritance - { + val Right(files) = + steps( + "Pravda.dll" -> Seq("PravdaDotNet/Pravda.cs"), + "Inheritance.exe" -> Seq("Pravda.dll", "dotnet-tests/resources/Inheritance.cs") + ).run + + val pe = files.last.parsedPe val parentCls = TypeDefData( @@ -22,8 +29,8 @@ object InheritanceTests extends TestSuite { Vector(), Vector( MethodDefData(0, 0, 6278, ".ctor", 1, Vector(ParamData(0, 1, "val"))), - MethodDefData(1, 0, 454, "AnswerPlus1", 43, Vector()), - MethodDefData(2, 0, 454, "Answer", 43, Vector()) + MethodDefData(1, 0, 454, "AnswerPlus1", 34, Vector()), + MethodDefData(2, 0, 454, "Answer", 34, Vector()) ) ) @@ -38,9 +45,9 @@ object InheritanceTests extends TestSuite { "A", "", parentCls, - Vector(FieldData(1, "AVal", 40)), + Vector(FieldData(0, 1, "AVal", 31)), Vector(MethodDefData(3, 0, 6278, ".ctor", 1, Vector(ParamData(0, 1, "aVal"))), - MethodDefData(4, 0, 198, "Answer", 43, Vector())) + MethodDefData(4, 0, 198, "Answer", 34, Vector())) ), TypeDefData( 3, @@ -48,19 +55,19 @@ object InheritanceTests extends TestSuite { "B", "", parentCls, - Vector(FieldData(1, "BVal", 40)), + Vector(FieldData(1, 1, "BVal", 31)), Vector(MethodDefData(5, 0, 6278, ".ctor", 1, Vector(ParamData(0, 1, "bVal"))), - MethodDefData(6, 0, 198, "Answer", 43, Vector())) + MethodDefData(6, 0, 198, "Answer", 34, Vector())) ), TypeDefData( 4, 1048577, - "MyProgram", + "Inheritance", "", TypeRefData(6, "Object", "System"), Vector(), - Vector(MethodDefData(7, 0, 134, "Func", 43, Vector()), - MethodDefData(8, 0, 150, "Main", 47, Vector()), + Vector(MethodDefData(7, 0, 134, "TestInheritance", 34, Vector()), + MethodDefData(8, 0, 150, "Main", 38, Vector()), MethodDefData(9, 0, 6278, ".ctor", 6, Vector())) ) ) @@ -70,14 +77,14 @@ object InheritanceTests extends TestSuite { pe.cilData.tables.methodDefTable, Vector( MethodDefData(0, 0, 6278, ".ctor", 1, Vector(ParamData(0, 1, "val"))), - MethodDefData(1, 0, 454, "AnswerPlus1", 43, Vector()), - MethodDefData(2, 0, 454, "Answer", 43, Vector()), + MethodDefData(1, 0, 454, "AnswerPlus1", 34, Vector()), + MethodDefData(2, 0, 454, "Answer", 34, Vector()), MethodDefData(3, 0, 6278, ".ctor", 1, Vector(ParamData(0, 1, "aVal"))), - MethodDefData(4, 0, 198, "Answer", 43, Vector()), + MethodDefData(4, 0, 198, "Answer", 34, Vector()), MethodDefData(5, 0, 6278, ".ctor", 1, Vector(ParamData(0, 1, "bVal"))), - MethodDefData(6, 0, 198, "Answer", 43, Vector()), - MethodDefData(7, 0, 134, "Func", 43, Vector()), - MethodDefData(8, 0, 150, "Main", 47, Vector()), + MethodDefData(6, 0, 198, "Answer", 34, Vector()), + MethodDefData(7, 0, 134, "TestInheritance", 34, Vector()), + MethodDefData(8, 0, 150, "Main", 38, Vector()), MethodDefData(9, 0, 6278, ".ctor", 6, Vector()) ) ) diff --git a/dotnet/src/test/scala/pravda/dotnet/parser/MiscTests.scala b/dotnet/src/test/scala/pravda/dotnet/parser/MiscTests.scala deleted file mode 100644 index 0b785669..00000000 --- a/dotnet/src/test/scala/pravda/dotnet/parser/MiscTests.scala +++ /dev/null @@ -1,26 +0,0 @@ -package pravda.dotnet - -package parser - -import pravda.dotnet.data.Heaps.SequencePoint -import pravda.dotnet.data.TablesData._ -import utest._ - -// all *.exe files was compiled by csc *.cs - -object MiscTests extends TestSuite { - - val tests = Tests { - 'helloWorldParse - { - val Right(pdb) = parsePdbFile("hello_world.pdb") - - pdb.tablesData.methodDebugInformationTable ==> Vector( - MethodDebugInformationData(Some("/tmp/pravda/hello_world.cs"), - List(SequencePoint(0, 6, 5, 6, 6), - SequencePoint(1, 7, 9, 7, 43), - SequencePoint(12, 8, 5, 8, 6))), - MethodDebugInformationData(None, List()) - ) - } - } -} diff --git a/dotnet/src/test/scala/pravda/dotnet/parser/ObjectsTests.scala b/dotnet/src/test/scala/pravda/dotnet/parser/ObjectsTests.scala index 192ead5e..68e64774 100644 --- a/dotnet/src/test/scala/pravda/dotnet/parser/ObjectsTests.scala +++ b/dotnet/src/test/scala/pravda/dotnet/parser/ObjectsTests.scala @@ -3,14 +3,21 @@ package pravda.dotnet package parser import pravda.common.TestUtils +import pravda.dotnet.DotnetCompilation.dsl._ import pravda.dotnet.data.TablesData._ import utest._ object ObjectsTests extends TestSuite { val tests = Tests { - 'objectsParse - { - val Right(pe) = parsePeFile("objects.exe") + 'Object - { + val Right(files) = + steps( + "Pravda.dll" -> Seq("PravdaDotNet/Pravda.cs"), + "Objects.exe" -> Seq("Pravda.dll", "dotnet-tests/resources/Object.cs") + ).run + + val pe = files.last.parsedPe TestUtils.assertEqual( pe.cilData.tables.typeDefTable, @@ -22,9 +29,9 @@ object ObjectsTests extends TestSuite { "A", "", TypeRefData(6, "Object", "System"), - Vector(FieldData(1, "AVal", 38)), + Vector(FieldData(0, 1, "AVal", 30)), Vector(MethodDefData(0, 0, 6278, ".ctor", 1, Vector(ParamData(0, 1, "aVal"))), - MethodDefData(1, 0, 134, "AnswerA", 41, Vector())) + MethodDefData(1, 0, 134, "AnswerA", 33, Vector())) ), TypeDefData( 2, @@ -32,19 +39,19 @@ object ObjectsTests extends TestSuite { "B", "", TypeRefData(6, "Object", "System"), - Vector(FieldData(1, "BVal", 38)), + Vector(FieldData(1, 1, "BVal", 30)), Vector(MethodDefData(2, 0, 6278, ".ctor", 1, Vector(ParamData(0, 1, "bVal"))), - MethodDefData(3, 0, 134, "AnswerB", 41, Vector())) + MethodDefData(3, 0, 134, "AnswerB", 33, Vector())) ), TypeDefData( 3, 1048577, - "MyProgram", + "Object", "", TypeRefData(6, "Object", "System"), Vector(), - Vector(MethodDefData(4, 0, 134, "Func", 41, Vector()), - MethodDefData(5, 0, 150, "Main", 45, Vector()), + Vector(MethodDefData(4, 0, 134, "TestObjects", 33, Vector()), + MethodDefData(5, 0, 150, "Main", 37, Vector()), MethodDefData(6, 0, 6278, ".ctor", 6, Vector())) ) ) @@ -54,11 +61,11 @@ object ObjectsTests extends TestSuite { pe.cilData.tables.methodDefTable, Vector( MethodDefData(0, 0, 6278, ".ctor", 1, Vector(ParamData(0, 1, "aVal"))), - MethodDefData(1, 0, 134, "AnswerA", 41, Vector()), + MethodDefData(1, 0, 134, "AnswerA", 33, Vector()), MethodDefData(2, 0, 6278, ".ctor", 1, Vector(ParamData(0, 1, "bVal"))), - MethodDefData(3, 0, 134, "AnswerB", 41, Vector()), - MethodDefData(4, 0, 134, "Func", 41, Vector()), - MethodDefData(5, 0, 150, "Main", 45, Vector()), + MethodDefData(3, 0, 134, "AnswerB", 33, Vector()), + MethodDefData(4, 0, 134, "TestObjects", 33, Vector()), + MethodDefData(5, 0, 150, "Main", 37, Vector()), MethodDefData(6, 0, 6278, ".ctor", 6, Vector()) ) ) diff --git a/dotnet/src/test/scala/pravda/dotnet/parser/ProgramInterfaceTests.scala b/dotnet/src/test/scala/pravda/dotnet/parser/ProgramInterfaceTests.scala new file mode 100644 index 00000000..7424a3c7 --- /dev/null +++ b/dotnet/src/test/scala/pravda/dotnet/parser/ProgramInterfaceTests.scala @@ -0,0 +1,69 @@ +package pravda.dotnet.parser + +import pravda.common.TestUtils +import pravda.dotnet.DotnetCompilation.dsl.steps +import pravda.dotnet.DotnetCompilation.dsl._ +import pravda.dotnet.data.TablesData._ +import utest._ + +object ProgramInterfaceTests extends TestSuite { + + val tests = Tests { + 'ProgramInterface - { + val Right(files) = + steps( + "Pravda.dll" -> Seq("PravdaDotNet/Pravda.cs"), + "ProgramInterface.dll" -> Seq("Pravda.dll", "dotnet-tests/resources/ProgramInterface.cs") + ).run + + val pe = files.last.parsedPe + + TestUtils.assertEqual( + pe.cilData.tables.customAttributeTable, + Vector( + CustomAttributeData(Ignored, + MemberRefData(TypeRefData(6, + "CompilationRelaxationsAttribute", + "System.Runtime.CompilerServices"), + ".ctor", + 1)), + CustomAttributeData(Ignored, + MemberRefData(TypeRefData(6, + "RuntimeCompatibilityAttribute", + "System.Runtime.CompilerServices"), + ".ctor", + 6)), + CustomAttributeData(Ignored, + MemberRefData(TypeRefData(6, "DebuggableAttribute", "System.Diagnostics"), ".ctor", 10)), + CustomAttributeData( + TypeDefData( + 1, + 161, + "ProgramInterface", + "InterfaceNamespace", + Ignored, + Vector(), + Vector(MethodDefData(0, 0, 1478, "Add", 25, Vector(ParamData(0, 1, "a"), ParamData(0, 2, "b"))))), + MemberRefData(TypeRefData(10, "Program", "Expload.Pravda"), ".ctor", 6) + ), + CustomAttributeData( + TypeDefData( + 2, + 1048577, + "ProgramInterfaceImpl", + "InterfaceNamespace", + TypeRefData(6, "Object", "System"), + Vector(), + Vector( + MethodDefData(1, 0, 486, "Add", 25, Vector(ParamData(0, 1, "a"), ParamData(0, 2, "b"))), + MethodDefData(2, 0, 150, "Main", 31, Vector()), + MethodDefData(3, 0, 6278, ".ctor", 6, Vector()) + ) + ), + MemberRefData(TypeRefData(10, "Program", "Expload.Pravda"), ".ctor", 6) + ) + ) + ) + } + } +} diff --git a/dotnet/src/test/scala/pravda/dotnet/parser/SmartProgramTests.scala b/dotnet/src/test/scala/pravda/dotnet/parser/SmartProgramTests.scala index 9d98f9c3..b55caccb 100644 --- a/dotnet/src/test/scala/pravda/dotnet/parser/SmartProgramTests.scala +++ b/dotnet/src/test/scala/pravda/dotnet/parser/SmartProgramTests.scala @@ -3,6 +3,7 @@ package pravda.dotnet package parser import pravda.common.TestUtils +import pravda.dotnet.DotnetCompilation.dsl._ import pravda.dotnet.data.Heaps.SequencePoint import pravda.dotnet.data.TablesData._ import utest._ @@ -10,8 +11,17 @@ import utest._ object SmartProgramTests extends TestSuite { val tests = Tests { - 'smartProgramParse - { - val Right(pe) = parsePeFile("smart_program.exe") + 'SmartProgram - { + val Right(files) = + steps( + "Pravda.dll" -> Seq("PravdaDotNet/Pravda.cs"), + "SmartProgram.exe" -> Seq("Pravda.dll", "dotnet-tests/resources/SmartProgram.cs") + ).run + val clearedFiles = clearPathsInPdb(files) + val pe = clearedFiles.last.parsedPe + val pdb = clearedFiles.last.parsedPdb.get + + val src = "$PRAVDA_TMP_DIR/SmartProgram.cs" TestUtils.assertEqual( pe.cilData.tables.customAttributeTable, @@ -33,56 +43,44 @@ object SmartProgramTests extends TestSuite { CustomAttributeData( TypeDefData( 1, - 1048576, - "MyProgram", + 1048577, + "SmartProgram", "", TypeRefData(6, "Object", "System"), - Vector(FieldData(1, "balances", 64)), + Vector(FieldData(0, 1, "Balances", 55)), Vector( - MethodDefData(0, 0, 134, "balanceOf", 73, Vector(ParamData(0, 1, "tokenOwner"))), - MethodDefData(1, 0, 134, "transfer", 79, Vector(ParamData(0, 1, "to"), ParamData(0, 2, "tokens"))), - MethodDefData(2, 0, 150, "Main", 86, Vector()), - MethodDefData(3, 0, 6278, ".ctor", 6, Vector()) + MethodDefData(0, 0, 134, "BalanceOf", 64, Vector(ParamData(0, 1, "tokenOwner"))), + MethodDefData(1, 0, 134, "Transfer", 70, Vector(ParamData(0, 1, "to"), ParamData(0, 2, "tokens"))), + MethodDefData(2, 0, 134, "Emit", 70, Vector(ParamData(0, 1, "owner"), ParamData(0, 2, "tokens"))), + MethodDefData(3, 0, 150, "Main", 77, Vector()), + MethodDefData(4, 0, 6278, ".ctor", 6, Vector()) ) ), MemberRefData(TypeRefData(10, "Program", "Expload.Pravda"), ".ctor", 6) ) ) ) - } - - 'smartProgramPdbParse - { - val Right(pdb) = parsePdbFile("smart_program.pdb") - val src = "/tmp/pravda/smart_program.cs" TestUtils.assertEqual( pdb.tablesData.methodDebugInformationTable, Vector( - MethodDebugInformationData(Some(src), - List(SequencePoint(0, 8, 44, 8, 45), - SequencePoint(1, 9, 9, 9, 51), - SequencePoint(17, 10, 5, 10, 6))), + MethodDebugInformationData(Some(src), List(SequencePoint(0, 11, 9, 11, 53))), MethodDebugInformationData( Some(src), - List( - SequencePoint(0, 12, 48, 12, 49), - SequencePoint(1, 13, 9, 13, 24), - SequencePoint(9, 13, 25, 13, 26), - SequencePoint(10, 14, 13, 14, 65), - SequencePoint(37, 14, 66, 14, 67), - SequencePoint(38, 15, 17, 15, 93), - SequencePoint(74, 16, 17, 16, 71), - SequencePoint(102, 17, 13, 17, 14), - SequencePoint(103, 18, 9, 18, 10), - SequencePoint(104, 19, 5, 19, 6) - ) + List(SequencePoint(0, 16, 9, 16, 24), + SequencePoint(4, 17, 13, 17, 67), + SequencePoint(24, 18, 17, 18, 92), + SequencePoint(59, 19, 17, 19, 70), + SequencePoint(86, 22, 5, 22, 6)) ), MethodDebugInformationData(Some(src), - List(SequencePoint(0, 21, 31, 21, 32), SequencePoint(1, 21, 32, 21, 33))), - MethodDebugInformationData(Some(src), List(SequencePoint(0, 6, 5, 6, 62))) + List(SequencePoint(0, 26, 9, 26, 24), + SequencePoint(4, 27, 13, 27, 72), + SequencePoint(31, 29, 5, 29, 6))), + MethodDebugInformationData(Some(src), List(SequencePoint(0, 31, 32, 31, 33))), + MethodDebugInformationData(Some(src), List(SequencePoint(0, 7, 5, 7, 70))) ) ) - } } } diff --git a/evm/src/main/scala/pravda/evm/EVM.scala b/evm/src/main/scala/pravda/evm/EVM.scala index 0e182686..65b4c949 100644 --- a/evm/src/main/scala/pravda/evm/EVM.scala +++ b/evm/src/main/scala/pravda/evm/EVM.scala @@ -83,7 +83,6 @@ object EVM { final case class Dup(n: Int) extends Op final case class Swap(n: Int) extends Op final case class Log(n: Int) extends Op - final case class JumpDest(n: Int) extends Op case object Create extends Op case object Call extends Op case object CallCode extends Op @@ -94,6 +93,25 @@ object EVM { case object Invalid extends Op case object SelfDestruct extends Op + trait AddressedJumpOp extends Op { + def addr: Int + } + final case class JumpDest(addr: Int) extends Op + case class SelfAddressedJump(addr: Int) extends AddressedJumpOp + case class SelfAddressedJumpI(addr: Int) extends AddressedJumpOp + case class Jump(addr: Int, dest: Int) extends AddressedJumpOp + case class JumpI(addr: Int, dest: Int) extends AddressedJumpOp + + case class MLoad(stackOffset: Int) extends Op + case class MStore(stackOffset: Int) extends Op + case class MStore8(stackOffset: Int) extends Op + + case class CallDataLoad(stackOffset: Int) extends Op + case class CallDataSize(stackOffset: Int) extends Op + + case class Sha3(stackOffset: Int) extends Op + case class Return(stackOffset: Int) extends Op + val singleOps: Map[Int, Op] = Map( 0x00 -> Stop, 0x01 -> Add, @@ -175,4 +193,15 @@ object EVM { (0xa0 to 0xa4, i => Log(i - 0xa0)) ) + sealed trait AbiType + + sealed trait Fixed extends AbiType + sealed trait Dynamic extends AbiType + + final case class UInt(bytes: Int) extends Fixed + final case class SInt(bytes: Int) extends Fixed + + final case object Bool extends Fixed + final case object Unsupported extends AbiType + } diff --git a/evm/src/main/scala/pravda/evm/abi/parse/AbiParser.scala b/evm/src/main/scala/pravda/evm/abi/parse/AbiParser.scala new file mode 100644 index 00000000..24e6464d --- /dev/null +++ b/evm/src/main/scala/pravda/evm/abi/parse/AbiParser.scala @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2018 Expload.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package pravda.evm.abi.parse + +import java.nio.charset.Charset + +import pravda.evm.EVM +import pravda.evm.EVM.{Bool, SInt, UInt, Unsupported} +import tethys._ +import tethys.jackson._ +import tethys.derivation.auto._ +import tethys.derivation.semiauto._ +import scala.annotation.tailrec + +object AbiParser { + + implicit val aboObjReader: JsonReader[AbiObject] = JsonReader.builder + .addField[String]("type") + .selectReader[AbiObject] { + case "function" => jsonReader[AbiFunction] + case "event" => jsonReader[AbiEvent] + case "constructor" => jsonReader[AbiConstructor] + } + + trait AbiObject { + def inputs: Seq[Argument] + + val arguments: List[(Int, EVM.AbiType)] = inputs + .map(variable => nameToType(variable.`type`)) + .foldLeft((4, List.empty[(Int, EVM.AbiType)])) { + case ((pos, types), varType) => + (pos + 32, (pos, varType) :: types) + } + ._2 + .reverse + } + + object AbiObject { + + def unwrap(abi: List[AbiObject]): (List[AbiFunction], List[AbiEvent], List[AbiConstructor]) = { + val funcs = abi.collect { case a: AbiFunction => a } + val events = abi.collect { case a: AbiEvent => a } + val constructors = abi.collect { case a: AbiConstructor => a } + (funcs, events, constructors) + } + } + + case class AbiFunction(constant: Boolean, + name: String, + inputs: Seq[Argument], + outputs: Seq[Argument], + payable: Boolean, + stateMutability: String, + newName: Option[String]) + extends AbiObject { + + lazy val hashableName = s"$name(${inputs.map(_.`type`).mkString(",")})" + lazy val id = hashKeccak256(hashableName).toList + } + + case class AbiEvent(name: String, inputs: Seq[Argument], anonymous: Boolean) extends AbiObject + case class AbiConstructor(inputs: Seq[Argument], payable: Boolean, stateMutability: String) extends AbiObject + case class Argument(name: String, `type`: String, indexed: Option[Boolean]) + + def nameToType(`type`: String): EVM.AbiType = `type` match { + case "bool" => Bool + case t if t.startsWith("uint") => UInt(t.substring(4).toInt) + case t if t.startsWith("int") => SInt(t.substring(3).toInt) + case "address" => UInt(160) + case _ => Unsupported + } + + def parseAbi(s: String): Either[String, List[AbiObject]] = s.jsonAs[List[AbiObject]] match { + case Right(functions) => + @tailrec + def buildUniqueNames(funcs: List[AbiFunction], + acc: List[AbiFunction], + names: Map[String, Int]): List[AbiFunction] = + funcs match { + case x :: xs => + if (names.contains(x.name)) + buildUniqueNames(xs, + x.copy(newName = Some(s"${x.name}${names(x.name)}")) :: acc, + names.updated(x.name, names(x.name) + 1)) + else buildUniqueNames(xs, x :: acc, names.updated(x.name, 0)) + case Nil => acc + } + + val (funcs, events, consts) = AbiObject.unwrap(functions) + Right(events ++ consts ++ buildUniqueNames(funcs, List.empty, Map.empty)) + case _ => Left(s"Invalid json $s") + } + + def hashKeccak256(s: String): Array[Byte] = { + import org.bouncycastle.jcajce.provider.digest._ + val digest = new Keccak.Digest256 + digest.digest(s.getBytes(Charset.forName("ASCII"))).slice(0, 4) + } +} diff --git a/evm/src/main/scala/pravda/evm/disasm/Blocks.scala b/evm/src/main/scala/pravda/evm/disasm/Blocks.scala new file mode 100644 index 00000000..48236d65 --- /dev/null +++ b/evm/src/main/scala/pravda/evm/disasm/Blocks.scala @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2018 Expload.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package pravda.evm.disasm + +import pravda.evm.EVM +import pravda.evm.EVM._ +import pravda.evm.translate.Translator.{ActualCode, Addressed, ContractCode, CreationCode} + +import scala.annotation.tailrec + +object Blocks { + + def split(ops: List[Op]): List[List[Op]] = { + @tailrec def split(ops: List[Op], acc: List[List[Op]]): List[List[Op]] = ops match { + case Nil => acc.reverse + case JumpDest :: xs => split(xs, List(JumpDest) :: acc) + case JumpDest(addr) :: xs => split(xs, List(JumpDest(addr)) :: acc.head.reverse :: acc.tail) + case SelfAddressedJump(n) :: xs => split(xs, Nil :: (SelfAddressedJump(n) :: acc.head).reverse :: acc.tail) + case SelfAddressedJumpI(n) :: xs => + split(xs, (SelfAddressedJumpI(n) :: Nil) :: (SelfAddressedJumpI(n) :: acc.head).reverse :: acc.tail) + + case Jump(addr, dest) :: xs => split(xs, Nil :: (Jump(addr, dest) :: acc.head).reverse :: acc.tail) + case JumpI(addr, dest) :: xs => + split(xs, (JumpI(addr, dest) :: Nil) :: (JumpI(addr, dest) :: acc.head).reverse :: acc.tail) + case Return :: xs => split(xs, Nil :: (Return :: acc.head).reverse :: acc.tail) + case SelfDestruct :: xs => split(xs, Nil :: (SelfDestruct :: acc.head).reverse :: acc.tail) + case Stop :: xs => split(xs, Nil :: (Stop :: acc.head).reverse :: acc.tail) + case Invalid :: xs => split(xs, Nil :: (Invalid :: acc.head).reverse :: acc.tail) + case Revert :: xs => split(xs, Nil :: (Revert :: acc.head).reverse :: acc.tail) + + case x :: xs => split(xs, (x :: acc.head) :: acc.tail) + } + split(ops, List(Nil)).filter(_.nonEmpty) + } + + case class WithJumpDest(dest: JumpDest, ops: List[Op]) + case class WithJumpI(jumpi: SelfAddressedJumpI, ops: List[Op]) + case class Jumpable(withJumpdest: List[WithJumpDest], withoutJumpdest: List[List[Op]]) + + def jumpable(blocks: List[List[Op]]): Jumpable = { + val (withJ, withoutJ) = blocks.partition { + case JumpDest(addr) :: xs => true + case xs => false + } + Jumpable(withJ.collect { + case JumpDest(addr) :: xs => WithJumpDest(JumpDest(addr), xs) + }, withoutJ) + } + + def continuation(blocks: List[List[Op]]): List[WithJumpI] = { + blocks.collect { + case SelfAddressedJumpI(addr) :: xs => WithJumpI(SelfAddressedJumpI(addr), xs) + } + } + + def splitToCreativeAndRuntime(ops: List[Addressed[EVM.Op]]): Either[String, ContractCode] = { + val length = ops.last._1.toLong + val blocks = Blocks.split(ops.map(_._2)) + val offsetOpt: Option[Int] = blocks + .map(bl => Emulator.eval(bl, new StackList(List.empty[StackItem]), List.empty[HistoryRecord])) + .flatMap { case (s, h) => h.collect { case r @ HistoryRecord(CodeCopy, _ :: Number(n) :: _) => n } } + .find(_ < length) + .map(_.toInt) + + offsetOpt.map { offset => + val (creative, runtime) = ops.partition(_._1 - offset < 0) + + val addressedCreative = creative.map { + case (ind, JumpDest) => ind -> JumpDest(ind) + case (ind, x) => ind -> x + } + + val addressedRuntime = runtime.map { + case (ind, JumpDest(_)) => (ind - offset) -> JumpDest(ind - offset) + case (ind, JumpDest) => (ind - offset) -> JumpDest(ind - offset) + case (ind, SelfAddressedJump(_)) => (ind - offset) -> SelfAddressedJump(ind - offset) + case (ind, SelfAddressedJumpI(_)) => (ind - offset) -> SelfAddressedJumpI(ind - offset) + case (ind, Jump) => (ind - offset) -> SelfAddressedJump(ind - offset) + case (ind, JumpI) => (ind - offset) -> SelfAddressedJumpI(ind - offset) + + case (ind, x) => (ind - offset) -> x + } + (CreationCode(addressedCreative), ActualCode(addressedRuntime)) + } toRight "Can't split to creative and runtime" + } + +} diff --git a/evm/src/main/scala/pravda/evm/disasm/Emulator.scala b/evm/src/main/scala/pravda/evm/disasm/Emulator.scala new file mode 100644 index 00000000..7d11c329 --- /dev/null +++ b/evm/src/main/scala/pravda/evm/disasm/Emulator.scala @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2018 Expload.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package pravda.evm.disasm + +import pravda.evm.EVM.{AddressedJumpOp, Op, _} +import pravda.evm.disasm.Blocks.{WithJumpDest, WithJumpI} + +import scala.annotation.tailrec + +trait StackItem + +trait Expr extends StackItem + +case class Expr0(op: Op) extends Expr + +case class Expr1(op: Op, operand1: StackItem) extends Expr + +case class Expr2(op: Op, operand1: StackItem, operand2: StackItem) extends Expr + +case class Expr3(op: Op, operand1: StackItem, operand2: StackItem, operand3: StackItem) extends Expr + +case class Number(n: BigInt) extends StackItem + +case class CommandResult(op: Op, numb: Int) extends StackItem + +case class HistoryRecord(op: Op, args: List[StackItem]) + +object Emulator { + + @tailrec def eval(ops: List[Op], + state: Stack[StackItem], + history: List[HistoryRecord]): (Stack[StackItem], List[HistoryRecord]) = { + ops match { + case Nil => state -> history + case Push(n) :: xs => eval(xs, state push Number(BigInt(1, n.toArray)), HistoryRecord(Push(n), Nil) :: history) + case Swap(n) :: xs if n < state.size => eval(xs, state swap n, history) + case Dup(n) :: xs if n <= state.size => eval(xs, state dup n, history) + + case op :: ops => + val r = OpCodes.stackReadCount(op) + val (args, s) = if (state.size >= r) state.pop(r) else state.pop(state.size) + val w = OpCodes.stackWriteCount(op) + val resState = (1 to w).foldLeft(s) { case (state, n) => state push CommandResult(op, n) } + eval(ops, resState, HistoryRecord(op, args) :: history) + } + } + + def eval(main: List[List[Op]], + withJ: Map[Int, WithJumpDest], + WithJumpI: Map[Int, WithJumpI]): (Set[AddressedJumpOp], Set[WithJumpDest]) = { + + def evalBlock(state: Stack[StackItem])(history: List[HistoryRecord])( + block: List[Op]): (Option[AddressedJumpOp], Stack[StackItem], List[HistoryRecord]) = { + val (newStack, newHistory) = Emulator.eval(block, state, history) + jump(newHistory) match { + case Some(HistoryRecord(SelfAddressedJumpI(n), Number(dest) :: _)) => + (Some(JumpI(n, dest.intValue())), newStack, Nil) + case Some(HistoryRecord(SelfAddressedJump(n), Number(dest) :: _)) => + (Some(Jump(n, dest.intValue())), newStack, Nil) + + case Some(r @ HistoryRecord(SelfAddressedJump(n), _)) => + (None, newStack, newHistory) + case Some(r @ HistoryRecord(SelfAddressedJumpI(n), _)) => + (None, newStack, newHistory) + + case _ => (None, newStack, newHistory) + } + } + + def evalChain(state: Stack[StackItem])(history: List[HistoryRecord])( + ops: List[Op], + withJ: Map[Int, WithJumpDest], + WithJumpI: Map[Int, WithJumpI], + acc: Set[AddressedJumpOp]): (Set[AddressedJumpOp], Map[Int, WithJumpDest], Map[Int, WithJumpI]) = { + val (jump, newStack, newHistory) = evalBlock(state)(history)(ops) + jump match { + case Some(j @ Jump(addr, dest)) if withJ.contains(dest) => + evalChain(newStack)(newHistory)(withJ(dest).ops, withJ - dest, WithJumpI, acc + j) + + case Some(j @ JumpI(addr, dest)) if withJ.contains(dest) && !WithJumpI.contains(addr) => + evalChain(newStack)(newHistory)(withJ(dest).ops, withJ - dest, WithJumpI, acc + j) + + case Some(j @ JumpI(addr, dest)) if withJ.contains(dest) && WithJumpI.contains(addr) => + val (jumps1, dests1, contins1) = + evalChain(newStack)(newHistory)(withJ(dest).ops, withJ - dest, WithJumpI - addr, acc + j) + val (jumps2, dests2, contins2) = + evalChain(newStack)(newHistory)(WithJumpI(addr).ops, withJ - dest, WithJumpI - addr, acc + j) + val dests = dests1.keySet.intersect(dests2.keySet).map(k => k -> dests1(k)).toMap + val withJumpi = contins1.keySet.intersect(contins2.keySet).map(k => k -> contins1(k)).toMap + (jumps1 ++ jumps2 ++ acc, dests, withJumpi) + + case Some(j @ Jump(addr, dest)) => (acc + j, withJ, WithJumpI) + + case Some(j @ JumpI(addr, dest)) => (acc + j, withJ, WithJumpI) + case _ => (acc, withJ, WithJumpI) + } + } + + val (jumps1, jumpDests1, jumpi1) = main.foldLeft((Set.empty[AddressedJumpOp], withJ, WithJumpI)) { + case ((jumps, dests, contins), ops) => + evalChain(StackList.empty)(List.empty)(ops, dests, contins, jumps) + } + val (jumps2, jumpDests2, jumpi2) = jumpi1.foldLeft((jumps1, jumpDests1, WithJumpI)) { + case ((jumps, dests, contins), (_, ops)) => + evalChain(StackList.empty)(List.empty)(ops.ops, dests, contins, jumps) + } + val (jumps3, jumpDests3, jumpi3) = jumpDests2.foldLeft((jumps2, jumpDests2, jumpi2)) { + case ((jumps, dests, contins), (_, ops)) => + evalChain(StackList.empty)(List.empty)(ops.ops, dests, contins, jumps) + } + jumps3 -> jumpDests3.values.toSet + } + + def jump(jump: List[HistoryRecord]): Option[HistoryRecord] = + jump.collectFirst { + case h @ HistoryRecord(SelfAddressedJumpI(_), _) => h + case h @ HistoryRecord(SelfAddressedJump(_), _) => h + } + + def jumps(blocks: List[List[Op]]): (Set[AddressedJumpOp], Set[WithJumpDest]) = { + val jumpable = Blocks.jumpable(blocks) + val main = jumpable.withoutJumpdest.filter { + case SelfAddressedJumpI(_) :: _ => false + case _ => true + } + val byJumpdest = jumpable.withJumpdest.groupBy(_.dest).map { case (k, v) => k.addr -> v.head } + val byJumpi = Blocks.continuation(blocks).groupBy(_.jumpi).map { case (k, v) => k.addr -> v.head } + Emulator.eval(main, byJumpdest, byJumpi) + } +} diff --git a/evm/src/main/scala/pravda/evm/disasm/JumpTargetRecognizer.scala b/evm/src/main/scala/pravda/evm/disasm/JumpTargetRecognizer.scala new file mode 100644 index 00000000..c72283ca --- /dev/null +++ b/evm/src/main/scala/pravda/evm/disasm/JumpTargetRecognizer.scala @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2018 Expload.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package pravda.evm.disasm + +import pravda.evm.EVM._ +import pravda.evm.disasm.Blocks.WithJumpDest +import pravda.evm.translate.Translator._ + +object JumpTargetRecognizer { + + def apply(ops: EvmCode): Either[Set[WithJumpDest], List[Addressed[Op]]] = { + + val blocks = Blocks.split(ops.code.map(_._2)) + val (jumps, jumpdests) = Emulator.jumps(blocks) + + val jumpsMap: Map[Int, AddressedJumpOp] = jumps.map { + case j @ JumpI(addr, _) => addr -> j + case j @ Jump(addr, _) => addr -> j + }.toMap + + val newOps = ops.code.map { + case (ind, SelfAddressedJumpI(ind1)) if jumpsMap.contains(ind1) => ind -> jumpsMap(ind1) + case (ind, SelfAddressedJump(ind1)) if jumpsMap.contains(ind1) => ind -> jumpsMap(ind1) + case a => a + } + + if (jumpdests.isEmpty) + Right(newOps) + else Left(jumpdests) + } +} diff --git a/evm/src/main/scala/pravda/evm/disasm/OpCodes.scala b/evm/src/main/scala/pravda/evm/disasm/OpCodes.scala new file mode 100644 index 00000000..6f985454 --- /dev/null +++ b/evm/src/main/scala/pravda/evm/disasm/OpCodes.scala @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2018 Expload.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package pravda.evm.disasm + +import pravda.evm.EVM +import pravda.evm.EVM._ + +object OpCodes { + + val terminate: PartialFunction[EVM.Op, Boolean] = { + + case Return => true + case SelfDestruct => true + case Stop => true + case Invalid => true + case Revert => true + case _ => false + } + + def handle(op: EVM.Op, size: Int): Int = size - stackReadCount(op) + stackWriteCount(op) + + val stackReadCount: PartialFunction[EVM.Op, Int] = { + case Dup(n) => n + case Swap(n) => n + 1 + case Log(n) => n + 2 + + case Not => 1 + case IsZero => 1 + case SignExtend => 1 + case Balance => 1 + case CallDataLoad => 1 + case BlockHash => 1 + case ExtCodeSize => 1 + case Pop => 1 + case MLoad => 1 + case SLoad => 1 + case Jump => 1 + case SelfDestruct => 1 + case SelfAddressedJump(_) => 1 + case Jump(_, _) => 1 + + case Add => 2 + case Mul => 2 + case Sub => 2 + case Div => 2 + case SDiv => 2 + case Mod => 2 + case SMod => 2 + case Exp => 2 + case Lt => 2 + case Gt => 2 + case Slt => 2 + case Sgt => 2 + case Eq => 2 + case And => 2 + case Or => 2 + case Xor => 2 + case Byte => 2 + case Sha3 => 2 + case MStore => 2 + case MStore8 => 2 + case SStore => 2 + case JumpI => 2 + case SelfAddressedJumpI(_) => 2 + case JumpI(_, _) => 2 + case Return => 2 + + case AddMod => 3 + case MulMod => 3 + case CallDataCopy => 3 + case CodeCopy => 3 + case Create => 3 + + case ExtCodeCopy => 4 + + case DelegateCall => 6 + + case Call => 7 + case CallCode => 7 + + case _ => 0 + } + + val stackWriteCount: PartialFunction[EVM.Op, Int] = { + case Stop => 0 + case CallDataCopy => 0 + case CodeCopy => 0 + case ExtCodeCopy => 0 + case Pop => 0 + case MStore => 0 + case MStore8 => 0 + case SStore => 0 + case Jump => 0 + case JumpI => 0 + case SelfAddressedJump(_) => 0 + case SelfAddressedJumpI(_) => 0 + case Jump(_, _) => 0 + case JumpI(_, _) => 0 + + case JumpDest => 0 + case JumpDest(_) => 0 + case Log(n) => 0 + case Return => 0 + case Invalid => 0 + case Revert => 0 + case SelfDestruct => 0 + + case Dup(n) => n + 1 + case Swap(n) => n + 1 + + case _ => 1 + } + +} diff --git a/evm/src/main/scala/pravda/evm/disasm/Stack.scala b/evm/src/main/scala/pravda/evm/disasm/Stack.scala new file mode 100644 index 00000000..b1c399c1 --- /dev/null +++ b/evm/src/main/scala/pravda/evm/disasm/Stack.scala @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2018 Expload.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package pravda.evm.disasm + +import scala.annotation.tailrec + +trait Stack[A] { self => + + def pop(n: Int): (List[A], Stack[A]) = { + @tailrec def aux(n: Int, acc: (List[A], Stack[A])): (List[A], Stack[A]) = n match { + case 0 => acc._1.reverse -> acc._2 + case _ => + val (el, tail) = acc._2.pop() + aux(n - 1, (el :: acc._1, tail)) + } + aux(n, (Nil, self)) + } + def pop(): (A, Stack[A]) + def push[B >: A](a: B): Stack[B] + def swap(a: Int): Stack[A] + def dup(a: Int): Stack[A] + def size: Int +} + +case class StackList[A](state: List[A]) extends Stack[A] { + lazy val size: Int = state.size + + def pop(): (A, Stack[A]) = state.head -> new StackList(state.tail) + def push[B >: A](a: B): Stack[B] = new StackList(a :: state) + + def swap(n: Int): Stack[A] = { + val (f, s) = state.splitAt(n) + StackList(s.head :: f.tail ::: f.head :: s.tail) + } + def dup(n: Int): Stack[A] = new StackList(state(n - 1) :: state) +} + +object StackList { + def empty[A]: Stack[A] = new StackList(List.empty[A]) + def apply[A](a: A*): Stack[A] = new StackList(a.toList) +} diff --git a/evm/src/main/scala/pravda/evm/disasm/StackSizePredictor.scala b/evm/src/main/scala/pravda/evm/disasm/StackSizePredictor.scala new file mode 100644 index 00000000..d51d55fe --- /dev/null +++ b/evm/src/main/scala/pravda/evm/disasm/StackSizePredictor.scala @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2018 Expload.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package pravda.evm.disasm + +import pravda.evm.EVM._ + +import scala.annotation.tailrec + +object StackSizePredictor { + + def clear(ops: List[(Op, Int)]): List[Op] = + ops.map { + case (MLoad, ind) => MLoad(ind) + case (MStore, ind) => MStore(ind) + case (MStore8, ind) => MStore8(ind) + case (CallDataSize, ind) => CallDataSize(ind) + case (CallDataLoad, ind) => CallDataLoad(ind) + case (Sha3, ind) => Sha3(ind) + case (op, _) => op + } + + def emulate(ops: List[Op]): List[(Op, Int)] = { + val ops1 = ops.map(_ -> -1).toArray + import OpCodes._ + type ToJumpDest = Option[Int] + type StackSize = Int + type ToContinue = Option[Int] + + @tailrec def emulate(ind: Int, size: Int): (ToJumpDest, ToContinue, StackSize) = { + ops1(ind) match { + case (x, s) if terminate(x) => + ops1(ind) = (x, size) + (None, None, handle(x, size)) + case (j @ Jump(addr, dest), s) => + ops1(ind) = (j, size) + (Some(dest), None, handle(j, size)) + case (j @ JumpI(addr, dest), s) => + ops1(ind) = (j, size) + (Some(dest), Some(ind + 1), handle(j, size)) + case (x, s) => + ops1(ind) = (x, size) + emulate(ind + 1, handle(x, size)) + } + } + + def emulateS(to: Int, size: Int): Unit = { + to match { + case ind if ind >= 0 => + val (to, cont, s) = emulate(ind, size) + to match { + case Some(addr) => + emulateS(ops1.indexWhere { + case (JumpDest(`addr`), i) if i < 0 => true + case _ => false + }, s) + case _ => + } + + cont match { + case Some(addr) if ops1(addr)._2 < 0 => emulateS(addr, s) + case _ => + } + case _ => + } + } + + emulateS(0, 0) + ops1.toList + } +} diff --git a/evm/src/main/scala/pravda/evm/parse/Parser.scala b/evm/src/main/scala/pravda/evm/parse/Parser.scala index 1c2072c2..f3e45a57 100644 --- a/evm/src/main/scala/pravda/evm/parse/Parser.scala +++ b/evm/src/main/scala/pravda/evm/parse/Parser.scala @@ -23,12 +23,28 @@ import pravda.evm.EVM._ import cats.syntax.traverse._ import cats.instances.list._ import cats.instances.either._ +import pravda.evm.translate.Translator.Addressed object Parser { - def apply(bytes: Bytes): Either[String, List[EVM.Op]] = { + def apply(bytes: Bytes): Either[String, List[EVM.Op]] = ops.parse(bytes).get.value - } + + def parseWithIndices(bytes: Array[Byte]): Either[String, List[Addressed[EVM.Op]]] = parseWithIndices(Bytes(bytes)) + + def parseWithIndices(bytes: Bytes): Either[String, List[Addressed[EVM.Op]]] = + opsWithIndices + .parse(bytes) + .get + .value + .toList + .map { case (i, e) => e.map(op => (i, op)) } + .sequence + .map(_.map { + case (ind, JumpI) => ind -> SelfAddressedJumpI(ind) + case (ind, Jump) => ind -> SelfAddressedJump(ind) + case a => a + }) private def push(cnt: Int): P[Push] = AnyByte.rep(exactly = cnt).!.map(Push) // FIXME the bytes default to zero if they extend past the limits diff --git a/evm/src/main/scala/pravda/evm/translate/Translator.scala b/evm/src/main/scala/pravda/evm/translate/Translator.scala index 9cd6f4e9..03f968f3 100644 --- a/evm/src/main/scala/pravda/evm/translate/Translator.scala +++ b/evm/src/main/scala/pravda/evm/translate/Translator.scala @@ -17,55 +17,157 @@ package pravda.evm.translate -import pravda.evm.EVM._ -import pravda.vm.asm import cats.instances.list._ import cats.instances.either._ import cats.syntax.traverse._ import pravda.evm.EVM -import pravda.evm.translate.opcode.{JumpDestinationPrepare, SimpleTranslation} +import pravda.evm.EVM._ +import pravda.evm.abi.parse.AbiParser +import pravda.evm.abi.parse.AbiParser.AbiObject +import pravda.evm.disasm.{Blocks, JumpTargetRecognizer, StackSizePredictor} +import pravda.evm.translate.opcode._ import pravda.vm.asm.Operation +import pravda.vm.{Data, Opcodes, asm} object Translator { - private val startLabelName = "__start_evm_program" + trait EvmCode { + def code: List[Addressed[EVM.Op]] + } + + case class CreationCode(code: List[Addressed[EVM.Op]]) extends EvmCode + case class ActualCode(code: List[Addressed[EVM.Op]]) extends EvmCode + case class Code(code: List[Addressed[EVM.Op]]) extends EvmCode - def apply(ops: List[EVM.Op]): Either[String, List[asm.Operation]] = { - ops.map(SimpleTranslation.evmOpToOps).sequence.map(_.flatten) + type Converted = Either[EVM.Op, List[asm.Operation]] + type Addressed[T] = (Int, T) + type ContractCode = (CreationCode, ActualCode) + + val startLabelName = "__start_evm_program" + val defaultMemorySize = 1024 + + def apply(ops: List[EVM.Op], abi: List[AbiObject]): Either[String, List[asm.Operation]] = { + val (funcs, _, _) = AbiObject.unwrap(abi) + FunctionSelectorTranslator + .evmToOps(ops, funcs) + .map { + case Left(op) => SimpleTranslation.evmOpToOps(op) + case Right(value) => Right(value) + } + .map(_.left.map(op => s"incorrect op: ${op.toString}")) + .sequence + .map(_.flatten) } - def translateActualContract(ops: List[(Int, EVM.Op)]): Either[String, List[asm.Operation]] = { - ops - .takeWhile({ - case (_, CodeCopy) => false - case _ => true - }) - .reverse - .tail - .headOption match { - case Some((_, Push(address))) => - val offset = BigInt(1, address.toArray).intValue() - - val filteredOps = ops - .map({ case (ind, op) => ind - offset -> op }) - .filterNot(_._1 < 0) - .map({ - case (ind, JumpDest) => JumpDest(ind) - case (_, op) => op - }) - - import JumpDestinationPrepare._ - val jumpDests = filteredOps.collect({ case j @ JumpDest(x) => j }).zipWithIndex - val prepare = jumpDests.flatMap(jumpDestToOps) - Translator(filteredOps).map( - opcodes => - Operation.Jump(Some(startLabelName)) - :: addLastBranch(prepare, jumpDests.size) ++ - (asm.Operation.Label(startLabelName) :: opcodes)) - - case _ => Left("Parse error") + def filterCode(ops: List[EVM.Op]): List[EVM.Op] = { + import fastparse.byte.all.Bytes + + ops match { + case Push(Bytes(-128)) :: + Push(Bytes(0x40)) :: + MStore(_) :: + rest => + filterCode(rest) + case Push(Bytes(0x04)) :: + CallDataSize(1) :: + Lt :: + Push(_: Bytes) :: + JumpI(_, _) + :: rest => + filterCode(rest) + case Push(Bytes(0x00)) :: + CallDataLoad(1) :: + Push(bs1: Bytes) :: + Swap(1) :: + Div :: + Push(bs2: Bytes) :: + And :: + rest + if bs1 == Bytes.fromHex("0x0100000000000000000000000000000000000000000000000000000000").get && + bs2 == Bytes.fromHex("0xffffffff").get => + filterCode(rest) + case Push(Bytes(0x00)) :: + CallDataLoad(1) :: + Push(bs1: Bytes) :: + Swap(1) :: + Div :: + rest if bs1 == Bytes.fromHex("0x0100000000000000000000000000000000000000000000000000000000").get => + filterCode(rest) + case CallValue :: + Dup(1) :: + IsZero :: + Push(_: Bytes) :: + JumpI(_, _) :: + Push(Bytes(0x00)) :: + Dup(1) :: + Revert :: rest => + filterCode(rest) + + case h :: t => h :: filterCode(t) + case _ => List.empty } + } + def translateActualContract(ops: List[Addressed[EVM.Op]], + abi: List[AbiObject]): Either[String, List[asm.Operation]] = { + for { + code1 <- Blocks.splitToCreativeAndRuntime(ops) + (creationCode1, actualContract1) = code1 + code2 <- JumpTargetRecognizer(actualContract1).left.map(_.toString) + ops = StackSizePredictor.clear(StackSizePredictor.emulate(code2.map(_._2))) + filtered = filterCode(ops) + res <- Translator(filtered, abi).map( + opcodes => + Operation.Label(startLabelName) :: + createArray(defaultMemorySize) ::: + Operation(Opcodes.SWAP) :: + opcodes ::: + StdlibAsm.stdlibFuncs.flatMap(_.code) ::: + convertResult(abi) + ) + } yield res } + private def createArray(size: Int): List[Operation] = + List( + pushInt(size), + pushType(Data.Type.Int8), + Operation(Opcodes.NEW_ARRAY) + ) + + private def convertResult(abi: List[AbiObject]): List[Operation] = { + val (funcs, _, _) = AbiObject.unwrap(abi) + + def castResult(arg: AbiParser.Argument): List[Operation] = AbiParser.nameToType(arg.`type`) match { + case EVM.UInt(num) => + num match { + case 8 => cast(Data.Type.Int16) + case 16 | 24 => cast(Data.Type.Int32) + case 32 | 40 | 48 | 56 => cast(Data.Type.Int64) + case _ => cast(Data.Type.BigInt) + } + case EVM.SInt(num) => + num match { + case 8 => cast(Data.Type.Int8) + case 16 => cast(Data.Type.Int16) + case 24 | 32 => cast(Data.Type.Int32) + case 40 | 48 | 56 | 64 => cast(Data.Type.Int64) + case _ => cast(Data.Type.BigInt) + } + case EVM.Bool => cast(Data.Type.Boolean) + case EVM.Unsupported => List.empty + } + + List(Operation.Label("convert_result")) ++ funcs.flatMap { f => + List( + Operation(Opcodes.DUP), + Operation.Push(Data.Primitive.Utf8(f.name)), + Operation(Opcodes.EQ), + Operation(Opcodes.NOT), + Operation.JumpI(Some(s"convert_result_not_${f.name}")) + ) ++ List(Operation(Opcodes.POP)) ++ + f.outputs.headOption.map(h => castResult(h)).toList.flatten ++ + List(Operation(Opcodes.STOP), Operation.Label(s"convert_result_not_${f.name}")) + } + } } diff --git a/evm/src/main/scala/pravda/evm/translate/opcode/FunctionSelectorTranslator.scala b/evm/src/main/scala/pravda/evm/translate/opcode/FunctionSelectorTranslator.scala new file mode 100644 index 00000000..fb15ebb3 --- /dev/null +++ b/evm/src/main/scala/pravda/evm/translate/opcode/FunctionSelectorTranslator.scala @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2018 Expload.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package pravda.evm.translate.opcode + +import pravda.evm.EVM +import pravda.evm.EVM._ +import pravda.evm.abi.parse.AbiParser.AbiFunction +import pravda.evm.translate.Translator.Converted +import pravda.vm.{Data, Opcodes} +import pravda.vm.asm.Operation + +object FunctionSelectorTranslator { + + private def createCallData(argsNum: Int): List[Operation] = + (argsNum + 2).to(3, -1).toList.flatMap(i => List(pushInt(i), Operation(Opcodes.SWAPN))) ++ + List(Operation(Opcodes.SWAP)) ++ + (argsNum + 1).to(2, -1).toList.flatMap(i => List(pushInt(i), Operation(Opcodes.SWAPN))) ++ + List(pushBytes(Array())) ++ + (1 to argsNum) + .flatMap( + i => + List(Operation(Opcodes.SWAP)) ::: + cast(Data.Type.Bytes) ::: + List(pushInt(8), Operation(Opcodes.SCALL), Operation(Opcodes.CONCAT)) + ) + .toList ++ + List(pushBytes(Array(0x0, 0x0, 0x0, 0x0)), + Operation(Opcodes.CONCAT), + Operation(Opcodes.SWAP), + Operation.Push(Data.Primitive.Null)) + + def evmToOps(ops: List[EVM.Op], abi: List[AbiFunction]): List[Converted] = { + + def aux(code: List[EVM.Op]): List[Converted] = { + import fastparse.byte.all.Bytes + + code match { + case (firstOp @ Dup(1)) :: + Push(Bytes(hash @ _*)) :: + Eq :: + Push(_: Bytes) :: + JumpI(_, addr) :: rest => + abi.find(_.id == hash) match { + case Some(f) => + Right( + codeToOps(Opcodes.DUP) ++ + List(pushString(f.newName.getOrElse(f.name))) ++ + codeToOps(Opcodes.EQ, Opcodes.NOT) ++ + List(Operation.JumpI(Some(s"not_${f.name}"))) ++ + createCallData(f.inputs.length) ++ + List(Operation.Jump(Some(nameByAddress(addr))), Operation.Label(s"not_${f.name}"))) :: aux(rest) + case None => Left(firstOp) :: aux(code.tail) + } + case h :: t => Left(h) :: aux(t) + case _ => List.empty + } + } + + aux(ops) + } +} diff --git a/evm/src/main/scala/pravda/evm/translate/opcode/JumpDestinationPrepare.scala b/evm/src/main/scala/pravda/evm/translate/opcode/JumpDestinationPrepare.scala index e88fbc5f..b2f380cd 100644 --- a/evm/src/main/scala/pravda/evm/translate/opcode/JumpDestinationPrepare.scala +++ b/evm/src/main/scala/pravda/evm/translate/opcode/JumpDestinationPrepare.scala @@ -19,9 +19,10 @@ package pravda.evm.translate.opcode import pravda.evm.EVM import pravda.evm.EVM.JumpDest +import pravda.evm.translate.Translator.startLabelName import pravda.vm.asm.Operation import pravda.vm.{Opcodes, asm} -import pravda.vm.asm.Operation.PushRef +import pravda.vm.asm.Operation.PushOffset object JumpDestinationPrepare { @@ -29,23 +30,29 @@ object JumpDestinationPrepare { op match { case (JumpDest(addr), ind) => List( - asm.Operation.Label(getNameByNumber(ind)), + asm.Operation.Label(nameByNumber(ind)), asm.Operation(Opcodes.DUP), pushBigInt(addr), asm.Operation(Opcodes.EQ), asm.Operation(Opcodes.NOT), - Operation.JumpI(Some(getNameByNumber(ind + 1))), + Operation.JumpI(Some(nameByNumber(ind + 1))), asm.Operation(Opcodes.POP), - PushRef(getNameByAddress(addr)), + PushOffset(nameByAddress(addr)), Operation.Jump(None) ) case _ => List() } - def addLastBranch(ops: List[asm.Operation], n: Int): List[asm.Operation] = - ops ++ (if (n > 0) - List(asm.Operation.Label(getNameByNumber(n)), - pushString("Incorrect destination"), - asm.Operation(Opcodes.THROW)) - else Nil) + def lastBranch(n: Int): List[asm.Operation] = + if (n > 0) + List(asm.Operation.Label(nameByNumber(n)), pushString("Incorrect destination"), asm.Operation(Opcodes.THROW)) + else Nil + + def jumpDestToAddressed(indexed: (Int, EVM.Op)): EVM.Op = indexed match { + case (ind, JumpDest) => JumpDest(ind) + case (_, op) => op + } + + def prepared(jumpDests: List[(JumpDest, Int)]): List[asm.Operation] = + Operation.Jump(Some(startLabelName)) :: jumpDests.flatMap(jumpDestToOps) ::: lastBranch(jumpDests.size) } diff --git a/evm/src/main/scala/pravda/evm/translate/opcode/SimpleTranslation.scala b/evm/src/main/scala/pravda/evm/translate/opcode/SimpleTranslation.scala index 99afdd9a..c0d19b2d 100644 --- a/evm/src/main/scala/pravda/evm/translate/opcode/SimpleTranslation.scala +++ b/evm/src/main/scala/pravda/evm/translate/opcode/SimpleTranslation.scala @@ -18,36 +18,47 @@ package pravda.evm.translate.opcode import pravda.evm.EVM +import pravda.evm.translate.Translator.Converted import pravda.vm.asm import pravda.vm._ import pravda.vm.asm.Operation +import pravda.evm.utils._ object SimpleTranslation { import pravda.evm.EVM._ + val pow2_256 = scala.BigInt(2).pow(256) - 1 + + private def bigintOps(asmOps: List[asm.Operation]): List[Operation] = + cast(Data.Type.BigInt) ++ + codeToOps(Opcodes.SWAP) ++ + cast(Data.Type.BigInt) ++ + codeToOps(Opcodes.SWAP) ++ + asmOps ++ + cast(Data.Type.Bytes) ++ + List(Operation.Push(Data.Primitive.Int8(8)), Operation(Opcodes.SCALL)) + + private def bigintOp(asmOp: asm.Operation): List[Operation] = + bigintOps(List(asmOp)) + private val translate: PartialFunction[EVM.Op, List[asm.Operation]] = { - case Push(bytes) => pushBigInt(BigInt(1, bytes.toArray)) :: Nil + case Push(bytes) => List(Operation.Push(evmWord(bytes.toArray))) case Pop => codeToOps(Opcodes.POP) - case Add => codeToOps(Opcodes.ADD) //FIXME result % 2^256 - case Mul => codeToOps(Opcodes.MUL) - case Div => codeToOps(Opcodes.DIV) //FIXME 0 if stack[1] == 0 othervise s[0] / s[1] - case Mod => codeToOps(Opcodes.MOD) //FIXME 0 if stack[1] == 0 othervise s[0] % s[1] - case Sub => sub //FIXME result & (2^256 - 1) - case AddMod => - dupn(3) ::: codeToOps(Opcodes.SWAP, Opcodes.MOD, Opcodes.SWAP) ::: dupn(3) ::: codeToOps(Opcodes.SWAP, - Opcodes.MOD, - Opcodes.ADD, - Opcodes.MOD) - case MulMod => - dupn(3) ::: codeToOps(Opcodes.SWAP, Opcodes.MOD, Opcodes.SWAP) ::: dupn(3) ::: codeToOps(Opcodes.SWAP, - Opcodes.MOD, - Opcodes.MUL, - Opcodes.MOD) - - // case Not => codeToOps(Opcodes.NOT) //TODO (2^256 - 1) - s[0] + case Add => bigintOp(Operation(Opcodes.ADD)) //FIXME result % 2^256 + case Mul => bigintOp(Operation(Opcodes.MUL)) + case Div => bigintOp(Operation(Opcodes.DIV)) //FIXME 0 if stack[1] == 0 othervise s[0] / s[1] + case Mod => bigintOp(Operation(Opcodes.MOD)) //FIXME 0 if stack[1] == 0 othervise s[0] % s[1] + case Sub => bigintOps(sub) //FIXME result & (2^256 - 1) +// case AddMod => +// dupn(3) ::: codeToOps(Opcodes.SWAP, Opcodes.MOD, Opcodes.SWAP) ::: dupn(3) ::: +// codeToOps(Opcodes.SWAP, Opcodes.MOD, Opcodes.ADD, Opcodes.MOD) +// case MulMod => +// dupn(3) ::: codeToOps(Opcodes.SWAP, Opcodes.MOD, Opcodes.SWAP) ::: dupn(3) ::: +// codeToOps(Opcodes.SWAP, Opcodes.MOD, Opcodes.MUL, Opcodes.MOD) + case And => codeToOps(Opcodes.AND) case Or => codeToOps(Opcodes.OR) case Xor => codeToOps(Opcodes.XOR) @@ -66,15 +77,16 @@ object SimpleTranslation { codeToOps(Opcodes.AND) ).flatten - case IsZero => pushBigInt(BigInt(0)) :: codeToOps(Opcodes.EQ) ::: cast(Data.Type.BigInt) - case Lt => codeToOps(Opcodes.LT) ::: cast(Data.Type.BigInt) - case Gt => codeToOps(Opcodes.GT) ::: cast(Data.Type.BigInt) - case Eq => codeToOps(Opcodes.EQ) ::: cast(Data.Type.BigInt) + case IsZero => + pushBytes(Array.fill[Byte](32)(0)) :: codeToOps(Opcodes.EQ) ::: + cast(Data.Type.Bytes) ++ List(Operation.Push(Data.Primitive.Int8(8)), Operation(Opcodes.SCALL)) + case Lt => bigintOp(Operation(Opcodes.LT)) + case Gt => bigintOp(Operation(Opcodes.GT)) + case Eq => bigintOp(Operation(Opcodes.EQ)) + case Jump(_, dest) => codeToOps(Opcodes.POP) ::: Operation.Jump(Some(nameByAddress(dest))) :: Nil + case JumpI(_, dest) => jumpi(dest) - case Jump => Operation.Jump(Some(getNameByNumber(0))) :: Nil - case JumpI => - codeToOps(Opcodes.SWAP) ::: cast(Data.Type.Boolean) ::: Operation.JumpI(Some(getNameByNumber(0))) :: Nil - case Stop => codeToOps(Opcodes.STOP) + case Stop => codeToOps(Opcodes.POP, Opcodes.POP, Opcodes.POP, Opcodes.STOP) case Dup(n) => if (n > 1) dupn(n) else codeToOps(Opcodes.DUP) case Swap(n) => if (n > 1) swapn(n + 1) else codeToOps(Opcodes.SWAP) @@ -82,12 +94,70 @@ object SimpleTranslation { case Balance => codeToOps(Opcodes.BALANCE) case Address => codeToOps(Opcodes.PADDR) - case JumpDest(address) => asm.Operation.Label(getNameByAddress(address)) :: Nil + case JumpDest(address) => asm.Operation.Label(nameByAddress(address)) :: Nil case SStore => codeToOps(Opcodes.SPUT) - case SLoad => codeToOps(Opcodes.SGET) + case SLoad => List(Operation.Call(Some("stdlib_evm_sget"))) + + case MLoad(offset) => + pushBigInt(scala.BigInt(32)) :: codeToOps(Opcodes.SWAP) ::: + cast(Data.Type.BigInt) ::: + pushInt(offset + 1) :: codeToOps(Opcodes.DUPN) ::: + List(Operation.Push(Data.Primitive.Int8(6)), Operation(Opcodes.SCALL)) + case MStore(offset) => + codeToOps(Opcodes.SWAP) ::: List(pushInt8(8)) ::: codeToOps(Opcodes.SCALL, Opcodes.SWAP) ::: + cast(Data.Type.BigInt) ::: pushInt(offset) :: codeToOps(Opcodes.DUPN) ::: + List(pushInt8(7), Operation(Opcodes.SCALL)) ::: + pushInt(offset - 1) :: codeToOps(Opcodes.SWAPN, Opcodes.POP) + + case MStore8(offset) => List(Operation.Meta(Meta.Custom(s"MStore8_$offset"))) + + case Not => pushBigInt(pow2_256) :: sub ::: Nil + case Revert => List(Operation.Push(Data.Primitive.Utf8("Revert")), Operation(Opcodes.THROW)) + case Return => + cast(Data.Type.BigInt) ::: + List(Operation(Opcodes.SWAP)) ::: + cast(Data.Type.BigInt) ::: + List(Operation(Opcodes.SWAP)) ::: + List( + pushInt(3), + Operation(Opcodes.DUPN), + pushInt8(6), + Operation(Opcodes.SCALL), + Operation(Opcodes.SWAP), + Operation(Opcodes.POP), + Operation(Opcodes.SWAP), + Operation(Opcodes.POP), + Operation(Opcodes.SWAP), + Operation.Jump(Some("convert_result")) + ) + + case CallValue => List(pushBytes(Array(0x01))) + case CallDataSize(offset) => + pushInt(offset + 1) :: codeToOps(Opcodes.DUPN, Opcodes.LENGTH) + case CallDataLoad(offset) => + cast(Data.Type.BigInt) ::: + pushInt(offset + 1) :: + codeToOps(Opcodes.DUPN, Opcodes.SWAP, Opcodes.DUP) ::: + pushInt(32) :: + codeToOps(Opcodes.ADD, Opcodes.SWAP, Opcodes.SLICE) + case Invalid => List(Operation.Push(Data.Primitive.Utf8("Invalid")), Operation(Opcodes.THROW)) + case Sha3(offset) => + cast(Data.Type.BigInt) ::: + codeToOps(Opcodes.SWAP) ::: + cast(Data.Type.BigInt) ::: + codeToOps(Opcodes.SWAP) ::: + pushInt(offset) :: + codeToOps(Opcodes.DUPN) ::: List( + pushInt8(6), + Operation(Opcodes.SCALL), + pushInt8(9), + Operation(Opcodes.SCALL) + ) + + case Caller => codeToOps(Opcodes.FROM) } - def evmOpToOps(op: EVM.Op): Either[String, List[asm.Operation]] = - translate.lift(op).toRight(s"Unknown opcode $op") + def evmOpToOps(op: EVM.Op): Converted = + translate.lift(op).toRight(op) } diff --git a/evm/src/main/scala/pravda/evm/translate/opcode/StdlibAsm.scala b/evm/src/main/scala/pravda/evm/translate/opcode/StdlibAsm.scala new file mode 100644 index 00000000..b7d114f9 --- /dev/null +++ b/evm/src/main/scala/pravda/evm/translate/opcode/StdlibAsm.scala @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2018 Expload.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package pravda.evm.translate.opcode + +import com.google.protobuf.ByteString +import pravda.vm.asm.Operation +import pravda.vm.{Data, Opcodes} + +object StdlibAsm { + final case class Function(name: String, code: List[Operation]) + + private def stdlibFunc(name: String, ops: List[Operation]): Function = { + val fname = s"stdlib_$name" + Function(fname, + List(Operation.Label(fname)) ++ + ops ++ List(Operation(Opcodes.RET))) + } + + val stdlibFuncs = List( + stdlibFunc( + "evm_sget", + List( + Operation(Opcodes.DUP), + Operation(Opcodes.SEXIST), + Operation.JumpI(Some("stdlib_evm_sget_non_zero")), + Operation(Opcodes.POP), + Operation.Push(Data.Primitive.Bytes(ByteString.copyFrom(Array.fill[Byte](32)(0)))), + Operation(Opcodes.RET), + Operation.Label("stdlib_evm_sget_non_zero"), + Operation(Opcodes.SGET) + ) + ) + ) + +} diff --git a/evm/src/main/scala/pravda/evm/translate/opcode/opcode.scala b/evm/src/main/scala/pravda/evm/translate/opcode/opcode.scala index fed37dae..820451c1 100644 --- a/evm/src/main/scala/pravda/evm/translate/opcode/opcode.scala +++ b/evm/src/main/scala/pravda/evm/translate/opcode/opcode.scala @@ -18,29 +18,35 @@ package pravda.evm.translate import com.google.protobuf.ByteString +import pravda.vm.asm.Operation import pravda.vm.{Data, Opcodes, asm} //TODO merge with pravda.dotnet.translation.opcode.opcode package object opcode { - def getNameByAddress(n: Int): String = s"_lbl_$n" - def getNameByNumber(n: Int): String = s"__switch_branch_$n" + def nameByAddress(n: Int): String = s"_lbl_$n" + def nameByNumber(n: Int): String = s"__switch_branch_$n" def push[T](value: T, toPrimitive: T => Data.Primitive): asm.Operation = asm.Operation.Push(toPrimitive(value)) - val sub - : List[asm.Operation] = asm.Operation(Opcodes.SWAP) :: pushBigInt(BigInt(-1)) :: asm.Operation(Opcodes.MUL) :: asm - .Operation(Opcodes.ADD) :: Nil + val sub: List[asm.Operation] = asm.Operation(Opcodes.SWAP) :: pushBigInt(BigInt(-1)) :: + asm.Operation(Opcodes.MUL) :: asm.Operation(Opcodes.ADD) :: Nil - val callExp: List[asm.Operation] = pushInt(3) :: asm.Operation(Opcodes.SCALL) :: Nil + val callExp: List[asm.Operation] = pushInt8(3) :: asm.Operation(Opcodes.SCALL) :: Nil def pushBigInt(value: scala.BigInt): asm.Operation = push(value, Data.Primitive.BigInt) + def pushInt8(b: Byte): asm.Operation = + push(b, Data.Primitive.Int8) + def pushInt(i: Int): asm.Operation = push(i, Data.Primitive.Int32) + def pushByte(i: Byte): asm.Operation = + push(i, Data.Primitive.Int8) + def pushFloat(d: Double): asm.Operation = push(d, Data.Primitive.Number) @@ -64,4 +70,23 @@ package object opcode { def codeToOps(codes: Int*): List[asm.Operation] = codes.map(asm.Operation(_)).toList + def splitBy[T](list: List[T], splitter: T => Boolean): List[List[T]] = { + list + .foldLeft((List.empty[T], List.empty[List[T]])) { + case ((current, acc), t) => + if (splitter(t)) (List.empty, current :: acc) + else (t :: current, acc) + } + ._2 + } + + def createArray(size: Int): List[Operation] = + List( + pushInt(size), + pushType(Data.Type.Int8), + Operation(Opcodes.NEW_ARRAY) + ) + + def jumpi(addr: Int): List[asm.Operation] = + codeToOps(Opcodes.POP) ++ cast(Data.Type.Boolean) ++ List(Operation.JumpI(Some(nameByAddress(addr)))) } diff --git a/evm/src/main/scala/pravda/evm/utils/package.scala b/evm/src/main/scala/pravda/evm/utils/package.scala new file mode 100644 index 00000000..87ee392f --- /dev/null +++ b/evm/src/main/scala/pravda/evm/utils/package.scala @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2018 Expload.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package pravda.evm + +import com.google.protobuf.ByteString +import pravda.vm.Data + +package object utils { + + def evmWord(arr: Array[Byte]): Data.Primitive.Bytes = + Data.Primitive.Bytes(ByteString.copyFrom(arr).concat(ByteString.copyFrom(Array.fill[Byte](32 - arr.length)(0)))) +} diff --git a/evm/src/test/resources/SimpleStorage.bin-runtime b/evm/src/test/resources/SimpleStorage.bin-runtime deleted file mode 100644 index d5d59ca4..00000000 --- a/evm/src/test/resources/SimpleStorage.bin-runtime +++ /dev/null @@ -1 +0,0 @@ -6080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a72305820954b2babaff031c0fa6279aa1e5991f97945657500d956eb391be1fc743e6b5d0029 \ No newline at end of file diff --git a/evm/src/test/resources/SimpleStorage/SimpleStorage.abi b/evm/src/test/resources/SimpleStorage/SimpleStorage.abi new file mode 100644 index 00000000..cd6cf662 --- /dev/null +++ b/evm/src/test/resources/SimpleStorage/SimpleStorage.abi @@ -0,0 +1 @@ +[{"constant":false,"inputs":[{"name":"x","type":"uint256"}],"name":"set","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"get","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}] \ No newline at end of file diff --git a/evm/src/test/resources/SimpleStorage/SimpleStorage.bin b/evm/src/test/resources/SimpleStorage/SimpleStorage.bin new file mode 100644 index 00000000..5db16e84 --- /dev/null +++ b/evm/src/test/resources/SimpleStorage/SimpleStorage.bin @@ -0,0 +1 @@ +608060405234801561001057600080fd5b5060ec8061001f6000396000f3fe6080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146085575b600080fd5b348015605957600080fd5b50608360048036036020811015606e57600080fd5b810190808035906020019092919050505060ad565b005b348015609057600080fd5b50609760b7565b6040518082815260200191505060405180910390f35b8060008190555050565b6000805490509056fea165627a7a7230582072b2ec1183e3e75a2354a90560878537cad9f10178943831c9137e890e6eb0400029 \ No newline at end of file diff --git a/evm/src/test/resources/SimpleStorage.evm b/evm/src/test/resources/SimpleStorage/SimpleStorage.evm similarity index 100% rename from evm/src/test/resources/SimpleStorage.evm rename to evm/src/test/resources/SimpleStorage/SimpleStorage.evm diff --git a/evm/src/test/resources/SimpleStorage.sl b/evm/src/test/resources/SimpleStorage/SimpleStorage.sl similarity index 89% rename from evm/src/test/resources/SimpleStorage.sl rename to evm/src/test/resources/SimpleStorage/SimpleStorage.sl index 525e03ed..38935b71 100644 --- a/evm/src/test/resources/SimpleStorage.sl +++ b/evm/src/test/resources/SimpleStorage/SimpleStorage.sl @@ -1,4 +1,4 @@ -pragma solidity ^0.4.0; +pragma solidity ^0.5.0; contract SimpleStorage { uint storedData; diff --git a/evm/src/test/resources/SimpleToken/SimpleToken.abi b/evm/src/test/resources/SimpleToken/SimpleToken.abi new file mode 100644 index 00000000..2afb01d0 --- /dev/null +++ b/evm/src/test/resources/SimpleToken/SimpleToken.abi @@ -0,0 +1 @@ +[{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"balances","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"tokenOwner","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"to","type":"address"},{"name":"token","type":"uint256"}],"name":"transfer","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"owner","type":"address"},{"name":"token","type":"uint256"}],"name":"emitTokens","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"}] \ No newline at end of file diff --git a/evm/src/test/resources/SimpleToken/SimpleToken.bin b/evm/src/test/resources/SimpleToken/SimpleToken.bin new file mode 100644 index 00000000..e7faecb5 --- /dev/null +++ b/evm/src/test/resources/SimpleToken/SimpleToken.bin @@ -0,0 +1 @@ +608060405234801561001057600080fd5b50610444806100206000396000f3fe608060405260043610610062576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806327e235e31461006757806370a08231146100cc578063a9059cbb14610131578063f11b9fc8146101a4575b600080fd5b34801561007357600080fd5b506100b66004803603602081101561008a57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610217565b6040518082815260200191505060405180910390f35b3480156100d857600080fd5b5061011b600480360360208110156100ef57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061022f565b6040518082815260200191505060405180910390f35b34801561013d57600080fd5b5061018a6004803603604081101561015457600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610277565b604051808215151515815260200191505060405180910390f35b3480156101b057600080fd5b506101fd600480360360408110156101c757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610389565b604051808215151515815260200191505060405180910390f35b60006020528060005260406000206000915090505481565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b6000816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054036000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054016000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055506001905092915050565b6000816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054016000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550600190509291505056fea165627a7a7230582044905d2bdb83b7595045b1753cf759bccc911ca15ee5e54dcc0c5ffd4d55eaf30029 \ No newline at end of file diff --git a/evm/src/test/resources/SimpleToken/SimpleToken.sl b/evm/src/test/resources/SimpleToken/SimpleToken.sl new file mode 100644 index 00000000..0475153d --- /dev/null +++ b/evm/src/test/resources/SimpleToken/SimpleToken.sl @@ -0,0 +1,26 @@ +pragma solidity ^0.5.0; + +contract SimpleToken { + + mapping(address => uint256) public balances; + + function balanceOf(address tokenOwner) public view returns (uint256 balance) + { + return balances[tokenOwner]; + } + + function transfer(address to, uint256 token) public returns (bool success) + { + balances[msg.sender] = balances[msg.sender] - token; + balances[to] = balances[to] + token; + + return true; + } + + function emitTokens(address owner, uint256 token) public returns (bool success) + { + balances[owner] = balances[owner] + token; + return true; + } +} + diff --git a/evm/src/test/resources/complex/ABIExampleWithOverloading.json b/evm/src/test/resources/complex/ABIExampleWithOverloading.json new file mode 100644 index 00000000..eac7a632 --- /dev/null +++ b/evm/src/test/resources/complex/ABIExampleWithOverloading.json @@ -0,0 +1,44 @@ +[ + { + "constant": false, + "inputs": [ + { + "name": "x", + "type": "uint256" + } + ], + "name": "set", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "get", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "x", + "type": "int256" + } + ], + "name": "set", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + } +] \ No newline at end of file diff --git a/evm/src/test/resources/complex/ComplexContract.bin b/evm/src/test/resources/complex/ComplexContract.bin new file mode 100644 index 00000000..e6854b1f --- /dev/null +++ b/evm/src/test/resources/complex/ComplexContract.bin @@ -0,0 +1 @@ +60606040526002805460ff19166012179055341561001c57600080fd5b604051610a9f380380610a9f833981016040528080519190602001805182019190602001805190910190505b60025460ff16600a0a83026003819055600160a060020a0333166000908152600460205260408120919091558280516100859291602001906100a3565b5060018180516100999291602001906100a3565b505b505050610143565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106100e457805160ff1916838001178555610111565b82800160010185558215610111579182015b828111156101115782518255916020019190600101906100f6565b5b5061011e929150610122565b5090565b61014091905b8082111561011e5760008155600101610128565b5090565b90565b61094d806101526000396000f300606060405236156100b75763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166306fdde0381146100bc578063095ea7b31461014757806318160ddd1461017d57806323b872dd146101a2578063313ce567146101de57806342966c681461020757806370a082311461023157806379cc67901461026257806395d89b4114610298578063a9059cbb14610323578063cae9ca5114610359578063dd62ed3e146103d2575b600080fd5b34156100c757600080fd5b6100cf610409565b60405160208082528190810183818151815260200191508051906020019080838360005b8381101561010c5780820151818401525b6020016100f3565b50505050905090810190601f1680156101395780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561015257600080fd5b610169600160a060020a03600435166024356104a7565b604051901515815260200160405180910390f35b341561018857600080fd5b6101906104d8565b60405190815260200160405180910390f35b34156101ad57600080fd5b610169600160a060020a03600435811690602435166044356104de565b604051901515815260200160405180910390f35b34156101e957600080fd5b6101f1610556565b60405160ff909116815260200160405180910390f35b341561021257600080fd5b61016960043561055f565b604051901515815260200160405180910390f35b341561023c57600080fd5b610190600160a060020a03600435166105b6565b60405190815260200160405180910390f35b341561026d57600080fd5b610169600160a060020a03600435166024356105c8565b604051901515815260200160405180910390f35b34156102a357600080fd5b6100cf61066f565b60405160208082528190810183818151815260200191508051906020019080838360005b8381101561010c5780820151818401525b6020016100f3565b50505050905090810190601f1680156101395780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561032e57600080fd5b610169600160a060020a036004351660243561070d565b604051901515815260200160405180910390f35b341561036457600080fd5b61016960048035600160a060020a03169060248035919060649060443590810190830135806020601f8201819004810201604051908101604052818152929190602084018383808284375094965061072495505050505050565b604051901515815260200160405180910390f35b34156103dd57600080fd5b610190600160a060020a0360043581169060243516610858565b60405190815260200160405180910390f35b60008054600181600116156101000203166002900480601f01602080910402602001604051908101604052809291908181526020018280546001816001161561010002031660029004801561049f5780601f106104745761010080835404028352916020019161049f565b820191906000526020600020905b81548152906001019060200180831161048257829003601f168201915b505050505081565b600160a060020a03338116600090815260056020908152604080832093861683529290522081905560015b92915050565b60035481565b600160a060020a0380841660009081526005602090815260408083203390941683529290529081205482111561051357600080fd5b600160a060020a038085166000908152600560209081526040808320339094168352929052208054839003905561054b848484610875565b5060015b9392505050565b60025460ff1681565b600160a060020a0333166000908152600460205260408120548290101561058557600080fd5b50600160a060020a03331660009081526004602052604090208054829003905560038054829003905560015b919050565b60046020526000908152604090205481565b600160a060020a038216600090815260046020526040812054829010156105ee57600080fd5b600160a060020a038084166000908152600560209081526040808320339094168352929052205482111561062157600080fd5b50600160a060020a0380831660009081526004602090815260408083208054869003905560058252808320339094168352929052208054829003905560038054829003905560015b92915050565b60018054600181600116156101000203166002900480601f01602080910402602001604051908101604052809291908181526020018280546001816001161561010002031660029004801561049f5780601f106104745761010080835404028352916020019161049f565b820191906000526020600020905b81548152906001019060200180831161048257829003601f168201915b505050505081565b600061071a338484610875565b5060015b92915050565b60008361073181856104a7565b1561084f5780600160a060020a0316638f4ffcb1338630876040518563ffffffff167c01000000000000000000000000000000000000000000000000000000000281526004018085600160a060020a0316600160a060020a0316815260200184815260200183600160a060020a0316600160a060020a0316815260200180602001828103825283818151815260200191508051906020019080838360005b838110156107e85780820151818401525b6020016107cf565b50505050905090810190601f1680156108155780820380516001836020036101000a031916815260200191505b5095505050505050600060405180830381600087803b151561083657600080fd5b6102c65a03f1151561084757600080fd5b505050600191505b5b509392505050565b600560209081526000928352604080842090915290825290205481565b6000600160a060020a038316151561088c57600080fd5b600160a060020a038416600090815260046020526040902054829010156108b257600080fd5b600160a060020a03831660009081526004602052604090205482810110156108d957600080fd5b50600160a060020a03828116600090815260046020526040808220805493871683529120805484810382558254850192839055905492019101811461091a57fe5b5b505050505600a165627a7a723058204dea1ba8e5dbc797ddb4126782e1313f9bb5bf51d9732cfcaf217efee15845570029 \ No newline at end of file diff --git a/evm/src/test/resources/complex/ComplexContract.sl b/evm/src/test/resources/complex/ComplexContract.sl new file mode 100644 index 00000000..e43c3fdd --- /dev/null +++ b/evm/src/test/resources/complex/ComplexContract.sl @@ -0,0 +1,158 @@ +pragma solidity ^0.4.16; + +interface tokenRecipient { function receiveApproval(address _from, uint256 _value, address _token, bytes _extraData) external; } + +contract TokenERC20 { + // Public variables of the token + string public name; + string public symbol; + uint8 public decimals = 18; + // 18 decimals is the strongly suggested default, avoid changing it + uint256 public totalSupply; + + // This creates an array with all balances + mapping (address => uint256) public balanceOf; + mapping (address => mapping (address => uint256)) public allowance; + + // This generates a public event on the blockchain that will notify clients + event Transfer(address indexed from, address indexed to, uint256 value); + + // This generates a public event on the blockchain that will notify clients + event Approval(address indexed _owner, address indexed _spender, uint256 _value); + + // This notifies clients about the amount burnt + event Burn(address indexed from, uint256 value); + + /** + * Constructor function + * + * Initializes contract with initial supply tokens to the creator of the contract + */ + function TokenERC20( + uint256 initialSupply, + string tokenName, + string tokenSymbol + ) public { + totalSupply = initialSupply * 10 ** uint256(decimals); // Update total supply with the decimal amount + balanceOf[msg.sender] = totalSupply; // Give the creator all initial tokens + name = tokenName; // Set the name for display purposes + symbol = tokenSymbol; // Set the symbol for display purposes + } + + /** + * Internal transfer, only can be called by this contract + */ + function _transfer(address _from, address _to, uint _value) internal { + // Prevent transfer to 0x0 address. Use burn() instead + require(_to != 0x0); + // Check if the sender has enough + require(balanceOf[_from] >= _value); + // Check for overflows + require(balanceOf[_to] + _value >= balanceOf[_to]); + // Save this for an assertion in the future + uint previousBalances = balanceOf[_from] + balanceOf[_to]; + // Subtract from the sender + balanceOf[_from] -= _value; + // Add the same to the recipient + balanceOf[_to] += _value; + + // Asserts are used to use static analysis to find bugs in your code. They should never fail + assert(balanceOf[_from] + balanceOf[_to] == previousBalances); + } + + /** + * Transfer tokens + * + * Send `_value` tokens to `_to` from your account + * + * @param _to The address of the recipient + * @param _value the amount to send + */ + function transfer(address _to, uint256 _value) public returns (bool success) { + _transfer(msg.sender, _to, _value); + return true; + } + + /** + * Transfer tokens from other address + * + * Send `_value` tokens to `_to` on behalf of `_from` + * + * @param _from The address of the sender + * @param _to The address of the recipient + * @param _value the amount to send + */ + function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) { + require(_value <= allowance[_from][msg.sender]); // Check allowance + allowance[_from][msg.sender] -= _value; + _transfer(_from, _to, _value); + return true; + } + + /** + * Set allowance for other address + * + * Allows `_spender` to spend no more than `_value` tokens on your behalf + * + * @param _spender The address authorized to spend + * @param _value the max amount they can spend + */ + function approve(address _spender, uint256 _value) public + returns (bool success) { + allowance[msg.sender][_spender] = _value; + + return true; + } + + /** + * Set allowance for other address and notify + * + * Allows `_spender` to spend no more than `_value` tokens on your behalf, and then ping the contract about it + * + * @param _spender The address authorized to spend + * @param _value the max amount they can spend + * @param _extraData some extra information to send to the approved contract + */ + function approveAndCall(address _spender, uint256 _value, bytes _extraData) + public + returns (bool success) { + tokenRecipient spender = tokenRecipient(_spender); + if (approve(_spender, _value)) { + spender.receiveApproval(msg.sender, _value, this, _extraData); + return true; + } + } + + /** + * Destroy tokens + * + * Remove `_value` tokens from the system irreversibly + * + * @param _value the amount of money to burn + */ + function burn(uint256 _value) public returns (bool success) { + require(balanceOf[msg.sender] >= _value); // Check if the sender has enough + balanceOf[msg.sender] -= _value; // Subtract from the sender + totalSupply -= _value; // Updates totalSupply + + return true; + } + + /** + * Destroy tokens from other account + * + * Remove `_value` tokens from the system irreversibly on behalf of `_from`. + * + * @param _from the address of the sender + * @param _value the amount of money to burn + */ + function burnFrom(address _from, uint256 _value) public returns (bool success) { + require(balanceOf[_from] >= _value); // Check if the targeted balance is enough + require(_value <= allowance[_from][msg.sender]); // Check allowance + balanceOf[_from] -= _value; // Subtract from the targeted balance + allowance[_from][msg.sender] -= _value; // Subtract from the sender's allowance + totalSupply -= _value; // Update totalSupply + + return true; + } +} diff --git a/evm/src/test/resources/complex/ComplexContractABI.json b/evm/src/test/resources/complex/ComplexContractABI.json new file mode 100644 index 00000000..59e3e5e7 --- /dev/null +++ b/evm/src/test/resources/complex/ComplexContractABI.json @@ -0,0 +1,295 @@ +[ + { + "constant": true, + "inputs": [], + "name": "name", + "outputs": [ + { + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_spender", + "type": "address" + }, + { + "name": "_value", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "name": "success", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_from", + "type": "address" + }, + { + "name": "_to", + "type": "address" + }, + { + "name": "_value", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "name": "success", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "decimals", + "outputs": [ + { + "name": "", + "type": "uint8" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_value", + "type": "uint256" + } + ], + "name": "burn", + "outputs": [ + { + "name": "success", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_from", + "type": "address" + }, + { + "name": "_value", + "type": "uint256" + } + ], + "name": "burnFrom", + "outputs": [ + { + "name": "success", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "symbol", + "outputs": [ + { + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_to", + "type": "address" + }, + { + "name": "_value", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_spender", + "type": "address" + }, + { + "name": "_value", + "type": "uint256" + }, + { + "name": "_extraData", + "type": "bytes" + } + ], + "name": "approveAndCall", + "outputs": [ + { + "name": "success", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "address" + }, + { + "name": "", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "name": "initialSupply", + "type": "uint256" + }, + { + "name": "tokenName", + "type": "string" + }, + { + "name": "tokenSymbol", + "type": "string" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "from", + "type": "address" + }, + { + "indexed": true, + "name": "to", + "type": "address" + }, + { + "indexed": false, + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "from", + "type": "address" + }, + { + "indexed": false, + "name": "value", + "type": "uint256" + } + ], + "name": "Burn", + "type": "event" + } +] \ No newline at end of file diff --git a/evm/src/test/resources/disasm/Contract3.bin b/evm/src/test/resources/disasm/Contract3.bin new file mode 100644 index 00000000..2f394d86 --- /dev/null +++ b/evm/src/test/resources/disasm/Contract3.bin @@ -0,0 +1 @@ +60806040526801a055690d9db8000034146100a157604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602160248201527f333020657468657220696e697469616c2066756e64696e67207265717569726560448201527f6400000000000000000000000000000000000000000000000000000000000000606482015290519081900360840190fd5b60028054600160a060020a033316600160a060020a03199091161790556000805460ff191690556102e5806100d76000396000f3006080604052600436106100775763ffffffff7c0100000000000000000000000000000000000000000000000000000000600035041663138fbe71811461007c5780632e1a7d4d146100a35780638da5cb5b146100bb578063b69ef8a8146100ec578063d0e30db014610101578063e65f2a7e14610109575b600080fd5b34801561008857600080fd5b5061009161011e565b60408051918252519081900360200190f35b3480156100af57600080fd5b5061009160043561012c565b3480156100c757600080fd5b506100d06101b3565b60408051600160a060020a039092168252519081900360200190f35b3480156100f857600080fd5b506100916101c2565b6100916101de565b34801561011557600080fd5b50610091610254565b600160a060020a0330163190565b600160a060020a033316600090815260016020526040812054821161019557600160a060020a033316600081815260016020526040808220805486900390555184156108fc0291859190818181858888f19350505050158015610193573d6000803e3d6000fd5b505b505033600160a060020a031660009081526001602052604090205490565b600254600160a060020a031681565b600160a060020a03331660009081526001602052604090205490565b600160a060020a0333166000818152600160209081526040808320805434908101909155815190815290519293927fa8126f7572bb1fdeae5b5aa9ec126438b91f658a07873f009d041ae690f3a193929181900390910190a250600160a060020a03331660009081526001602052604090205490565b60008054600360ff909116101561029c576000805460ff198116600160ff928316810190921617825533600160a060020a0316825260205260409020678ac7230489e8000090555b50600160a060020a033316600090815260016020526040902054905600a165627a7a72305820609de393c2679372b9150a57f6a72c28981cb2f558b5e41ff9e5e173e7644ad40029 \ No newline at end of file diff --git a/evm/src/test/resources/SimpleStorage.bin b/evm/src/test/resources/disasm/SimpleStorage.bin similarity index 100% rename from evm/src/test/resources/SimpleStorage.bin rename to evm/src/test/resources/disasm/SimpleStorage.bin diff --git a/evm/src/test/scala/pravda/evm/EvmSandbox.scala b/evm/src/test/scala/pravda/evm/EvmSandbox.scala new file mode 100644 index 00000000..996da531 --- /dev/null +++ b/evm/src/test/scala/pravda/evm/EvmSandbox.scala @@ -0,0 +1,32 @@ +package pravda.evm + +import pravda.evm.abi.parse.AbiParser.AbiObject +import pravda.evm.translate.Translator +import pravda.evm.translate.Translator.Addressed +import pravda.vm._ +import pravda.vm.asm.PravdaAssembler + +object EvmSandbox { + + def runCode(input: VmSandbox.Preconditions, + code: Seq[EVM.Op], + abi: Seq[AbiObject]): Either[String, VmSandbox.ExpectationsWithoutWatts] = { + val asmOps = Translator(code.toList, abi.toList) + val asmProgramE = asmOps.map(ops => PravdaAssembler.assemble(ops, saveLabels = true)) + + for { + asmProgram <- asmProgramE + } yield VmSandbox.ExpectationsWithoutWatts.fromExpectations(VmSandbox.run(input, asmProgram)) + } + + def runAddressedCode(input: VmSandbox.Preconditions, + code: Seq[Addressed[EVM.Op]], + abi: Seq[AbiObject]): Either[String, VmSandbox.ExpectationsWithoutWatts] = { + val asmOps = Translator.translateActualContract(code.toList, abi.toList) + val asmProgramE = asmOps.map(ops => PravdaAssembler.assemble(ops, saveLabels = true)) + + for { + asmProgram <- asmProgramE + } yield VmSandbox.ExpectationsWithoutWatts.fromExpectations(VmSandbox.run(input, asmProgram)) + } +} diff --git a/evm/src/test/scala/pravda/evm/abi/parser/ABIParserTests.scala b/evm/src/test/scala/pravda/evm/abi/parser/ABIParserTests.scala new file mode 100644 index 00000000..b8b3cc57 --- /dev/null +++ b/evm/src/test/scala/pravda/evm/abi/parser/ABIParserTests.scala @@ -0,0 +1,180 @@ +package pravda.evm + +package abi.parser + +import pravda.evm.abi.parse.AbiParser +import pravda.evm.abi.parse.AbiParser.{AbiConstructor, AbiEvent, AbiFunction, Argument} +import utest._ + +object ABIParserTests extends TestSuite { + + val tests = Tests { + + "ABI parse" - { + val abi = readSolidityABI("SimpleStorage/SimpleStorage.abi") + val parsedAbi = AbiParser.parseAbi(abi) + + parsedAbi ==> Right( + List( + AbiFunction(true, "get", Vector(), Vector(Argument("", "uint256", None)), false, "view", None), + AbiFunction(false, "set", Vector(Argument("x", "uint256", None)), Vector(), false, "nonpayable", None) + ) + ) + } + + 'SimpleToken - { + val abi = readSolidityABI("SimpleToken/SimpleToken.abi") + val parsedAbi = AbiParser.parseAbi(abi) + + parsedAbi ==> Right( + List( + AbiFunction( + false, + "emitTokens", + Vector(Argument("owner", "address", None), Argument("token", "uint256", None)), + Vector(Argument("success", "bool", None)), + false, + "nonpayable", + None + ), + AbiFunction( + false, + "transfer", + Vector(Argument("to", "address", None), Argument("token", "uint256", None)), + Vector(Argument("success", "bool", None)), + false, + "nonpayable", + None + ), + AbiFunction(true, + "balanceOf", + Vector(Argument("tokenOwner", "address", None)), + Vector(Argument("balance", "uint256", None)), + false, + "view", + None), + AbiFunction(true, + "balances", + Vector(Argument("", "address", None)), + Vector(Argument("", "uint256", None)), + false, + "view", + None) + ) + ) + } + + "Complex abi parse" - { + val abi = readSolidityABI("complex/ComplexContractABI.json") + val parsedAbi = AbiParser.parseAbi(abi) + + parsedAbi ==> Right( + List( + AbiEvent("Transfer", + Vector(Argument("from", "address", Some(true)), + Argument("to", "address", Some(true)), + Argument("value", "uint256", Some(false))), + false), + AbiEvent("Burn", + Vector(Argument("from", "address", Some(true)), Argument("value", "uint256", Some(false))), + false), + AbiConstructor(Vector(Argument("initialSupply", "uint256", None), + Argument("tokenName", "string", None), + Argument("tokenSymbol", "string", None)), + false, + "nonpayable"), + AbiFunction(true, + "allowance", + Vector(Argument("", "address", None), Argument("", "address", None)), + Vector(Argument("", "uint256", None)), + false, + "view", + None), + AbiFunction( + false, + "approveAndCall", + Vector(Argument("_spender", "address", None), + Argument("_value", "uint256", None), + Argument("_extraData", "bytes", None)), + Vector(Argument("success", "bool", None)), + false, + "nonpayable", + None + ), + AbiFunction(false, + "transfer", + Vector(Argument("_to", "address", None), Argument("_value", "uint256", None)), + Vector(), + false, + "nonpayable", + None), + AbiFunction(true, "symbol", Vector(), Vector(Argument("", "string", None)), false, "view", None), + AbiFunction( + false, + "burnFrom", + Vector(Argument("_from", "address", None), Argument("_value", "uint256", None)), + Vector(Argument("success", "bool", None)), + false, + "nonpayable", + None + ), + AbiFunction(true, + "balanceOf", + Vector(Argument("", "address", None)), + Vector(Argument("", "uint256", None)), + false, + "view", + None), + AbiFunction(false, + "burn", + Vector(Argument("_value", "uint256", None)), + Vector(Argument("success", "bool", None)), + false, + "nonpayable", + None), + AbiFunction(true, "decimals", Vector(), Vector(Argument("", "uint8", None)), false, "view", None), + AbiFunction( + false, + "transferFrom", + Vector(Argument("_from", "address", None), + Argument("_to", "address", None), + Argument("_value", "uint256", None)), + Vector(Argument("success", "bool", None)), + false, + "nonpayable", + None + ), + AbiFunction(true, "totalSupply", Vector(), Vector(Argument("", "uint256", None)), false, "view", None), + AbiFunction( + false, + "approve", + Vector(Argument("_spender", "address", None), Argument("_value", "uint256", None)), + Vector(Argument("success", "bool", None)), + false, + "nonpayable", + None + ), + AbiFunction(true, "name", Vector(), Vector(Argument("", "string", None)), false, "view", None) + ) + ) + } + + "Overloading abi parse" - { + val abi = readSolidityABI("complex/ABIExampleWithOverloading.json") + val parsedAbi = AbiParser.parseAbi(abi) + + parsedAbi ==> Right( + List( + AbiFunction(false, + "set", + Vector(Argument("x", "int256", None)), + Vector(), + false, + "nonpayable", + Option("set0")), + AbiFunction(true, "get", Vector(), Vector(Argument("", "uint256", None)), false, "view", None), + AbiFunction(false, "set", Vector(Argument("x", "uint256", None)), Vector(), false, "nonpayable", None) + )) + } + } +} diff --git a/evm/src/test/scala/pravda/evm/disasm/DisasmTests.scala b/evm/src/test/scala/pravda/evm/disasm/DisasmTests.scala new file mode 100644 index 00000000..79887d6c --- /dev/null +++ b/evm/src/test/scala/pravda/evm/disasm/DisasmTests.scala @@ -0,0 +1,50 @@ +package pravda.evm + +package disasm + +import java.io.File + +import pravda.evm.EVM._ +import pravda.evm.parse.Parser +import utest._ + +object DisasmTests extends TestSuite { + + val tests = Tests { + 'Disasm - { + new File(getClass.getResource("/disasm").getPath).listFiles.foreach { f => + val bytes = readSolidityBinFile(f) + val Right(ops) = Parser.parseWithIndices(bytes) + + Predef.assert( + !Blocks.splitToCreativeAndRuntime(ops).exists { + case (creative, runtime) => + JumpTargetRecognizer(creative).exists { ops1 => + JumpTargetRecognizer(runtime).exists { ops2 => + ops.zip(ops1 ::: ops2).exists { + case ((_, JumpI), (_, j)) => + j match { + case JumpI(_, _) => false + case _ => true + } + case ((_, Jump), (_, j)) => + j match { + case Jump(_, _) => false + case _ => true + } + case ((_, JumpDest), (_, j)) => + j match { + case JumpDest(_) => false + case _ => true + } + case _ => false + } + } + } + }, + s"Error in ${f.getAbsolutePath}" + ) + } + } + } +} diff --git a/evm/src/test/scala/pravda/evm/disasm/StackSizePredictorTests.scala b/evm/src/test/scala/pravda/evm/disasm/StackSizePredictorTests.scala new file mode 100644 index 00000000..958f69c8 --- /dev/null +++ b/evm/src/test/scala/pravda/evm/disasm/StackSizePredictorTests.scala @@ -0,0 +1,48 @@ +package pravda.evm + +package disasm + +import java.io.File + +import pravda.evm.EVM._ +import pravda.evm.parse.Parser +import utest._ + +object StackSizePredictorTests extends TestSuite { + + val tests = Tests { + + "Stack size prediction" - { + + new File(getClass.getResource("/disasm").getPath).listFiles.foreach { f => + val bytes = readSolidityBinFile(f) + val Right(ops) = Parser.parseWithIndices(bytes) + val Right((c, r)) = Blocks.splitToCreativeAndRuntime(ops) + + //TODO use product + val Right(opt1) = JumpTargetRecognizer(c).flatMap(a => JumpTargetRecognizer(r).map(b => (a, b))) + + Predef.assert( + opt1 match { + case (newOps1, newOps2) => + val res1 = StackSizePredictor.emulate(newOps1.map(_._2)) + val f = res1.forall { + case (_, ind) if ind >= 0 => true + case (Stop, -1) => true + case _ => false + } + + val res2 = StackSizePredictor.emulate(newOps2.map(_._2)) + val s = res2.forall { + case (_, ind) if ind >= 0 => true + case (Stop, -1) => true + case _ => false + } + s && f + }, + s"Error in ${f.getAbsolutePath}" + ) + } + } + } +} diff --git a/evm/src/test/scala/pravda/evm/disasm/StackTests.scala b/evm/src/test/scala/pravda/evm/disasm/StackTests.scala new file mode 100644 index 00000000..5f1dbee9 --- /dev/null +++ b/evm/src/test/scala/pravda/evm/disasm/StackTests.scala @@ -0,0 +1,29 @@ +package pravda.evm.disasm + +import utest._ + +object StackTests extends TestSuite { + + val tests = Tests { + 'Stack - { + val stack = StackList.empty[Int] push 1 push 2 push 3 + + stack.pop ==> Tuple2(3, StackList(2, 1)) + stack.pop._2.pop ==> Tuple2(2, StackList(1)) + + stack.pop(1) ==> Tuple2(List(3), StackList(2, 1)) + stack.pop(2) ==> Tuple2(List(3, 2), StackList(1)) + stack.pop(3) ==> Tuple2(List(3, 2, 1), StackList()) + + stack.swap(1) ==> StackList(2, 3, 1) + stack.swap(2) ==> StackList(1, 2, 3) + stack.swap(1).swap(1) ==> stack + stack.swap(1).swap(1).swap(2).swap(2) ==> stack + stack.swap(1).swap(2) ==> StackList(1, 3, 2) + + stack.dup(1) ==> StackList(3, 3, 2, 1) + stack.dup(2) ==> StackList(2, 3, 2, 1) + stack.dup(3) ==> StackList(1, 3, 2, 1) + } + } +} diff --git a/evm/src/test/scala/pravda/evm/evm.scala b/evm/src/test/scala/pravda/evm/evm.scala new file mode 100644 index 00000000..9177550f --- /dev/null +++ b/evm/src/test/scala/pravda/evm/evm.scala @@ -0,0 +1,30 @@ +package pravda + +import java.io.File + +import fastparse.byte.all._ + +import scala.io.Source + +package object evm { + private def readSolidityBinFileS(s: String): Bytes = { + val allBytes = Bytes(s.sliding(2, 2).toArray.map(Integer.parseInt(_, 16).toByte)) + allBytes.dropRight(43) + // for dropRight(43) see https://ethereum.stackexchange.com/questions/42584/what-is-auxdata-in-the-asm-output-from-solc + // we just drop auxdata + } + + def readSolidityBinFile(filename: String): Bytes = readSolidityBinFileS(Source.fromResource(filename).mkString) + + def readSolidityBinFile(file: File): Bytes = readSolidityBinFileS(Source.fromFile(file).mkString) + + def writeSolidityBinFile(filename: String): Bytes = { + + val s = Source.fromResource(filename).mkString + val allBytes = Bytes(s.sliding(2, 2).toArray.map(Integer.parseInt(_, 16).toByte)) + + allBytes + } + + def readSolidityABI(filename: String): String = Source.fromResource(filename).mkString +} diff --git a/evm/src/test/scala/pravda/evm/package.scala b/evm/src/test/scala/pravda/evm/package.scala deleted file mode 100644 index a9ec6179..00000000 --- a/evm/src/test/scala/pravda/evm/package.scala +++ /dev/null @@ -1,32 +0,0 @@ -package pravda - -import fastparse.byte.all._ -import pravda.vm.VmSandbox._ -import pravda.vm.{Data, VmSandbox} -import pravda.vm.asm.Operation - -import scala.collection.mutable.ArrayBuffer -import scala.io.Source - -package object evm { - - def run(either: Either[String, Seq[Operation]], pre: Preconditions): Either[String, Expectations] = - either.map(ops => VmSandbox.sandboxRun(ops, pre)) - - def expectations(watts: Long = 0L, - stack: Seq[Data.Primitive] = Nil, - heap: Map[Data.Primitive.Ref, Data] = Map.empty, - effects: Seq[EnvironmentEffect] = ArrayBuffer.empty, - events: Seq[EnviromentEvent] = ArrayBuffer.empty, - error: Option[String] = None): Expectations = - Expectations(watts, Memory(stack, heap), effects, events, error) - - def readSolidityBinFile(filename: String): Bytes = { - - val s = Source.fromResource(filename).mkString - val allBytes = Bytes(s.sliding(2, 2).toArray.map(Integer.parseInt(_, 16).toByte)) - allBytes.dropRight(43) - // for dropRight(43) see https://ethereum.stackexchange.com/questions/42584/what-is-auxdata-in-the-asm-output-from-solc - // we just drop auxdata - } -} diff --git a/evm/src/test/scala/pravda/evm/parse/EvmTests.scala b/evm/src/test/scala/pravda/evm/parse/EvmTests.scala deleted file mode 100644 index f3c0630f..00000000 --- a/evm/src/test/scala/pravda/evm/parse/EvmTests.scala +++ /dev/null @@ -1,157 +0,0 @@ -package pravda.evm.parse - -import fastparse.byte.all._ -import pravda.evm -import pravda.evm.EVM._ -import utest._ - -object EvmTests extends TestSuite { - - val tests = Tests { - 'SimpleStorage - { - val bytes = evm.readSolidityBinFile("SimpleStorage.bin") - - Parser(bytes) ==> - Right( - List( - Push(hex"0x80"), - Push(hex"0x40"), - MStore, - CallValue, - Dup(1), - IsZero, - Push(hex"0x0010"), - JumpI, - Push(hex"0x00"), - Dup(1), - Revert, - JumpDest, - Pop, - Push(hex"0xdf"), - Dup(1), - Push(hex"0x001f"), - Push(hex"0x00"), - CodeCopy, - Push(hex"0x00"), - Return, - Stop, - Push(hex"0x80"), - Push(hex"0x40"), - MStore, - Push(hex"0x04"), - CallDataSize, - Lt, - Push(hex"0x49"), - JumpI, - Push(hex"0x00"), - CallDataLoad, - Push(hex"0x0100000000000000000000000000000000000000000000000000000000"), - Swap(1), - Div, - Push(hex"0xffffffff"), - And, - Dup(1), - Push(hex"0x60fe47b1"), - Eq, - Push(hex"0x4e"), - JumpI, - Dup(1), - Push(hex"0x6d4ce63c"), - Eq, - Push(hex"0x78"), - JumpI, - JumpDest, - Push(hex"0x00"), - Dup(1), - Revert, - JumpDest, - CallValue, - Dup(1), - IsZero, - Push(hex"0x59"), - JumpI, - Push(hex"0x00"), - Dup(1), - Revert, - JumpDest, - Pop, - Push(hex"0x76"), - Push(hex"0x04"), - Dup(1), - CallDataSize, - Sub, - Dup(2), - Add, - Swap(1), - Dup(1), - Dup(1), - CallDataLoad, - Swap(1), - Push(hex"0x20"), - Add, - Swap(1), - Swap(3), - Swap(2), - Swap(1), - Pop, - Pop, - Pop, - Push(hex"0xa0"), - Jump, - JumpDest, - Stop, - JumpDest, - CallValue, - Dup(1), - IsZero, - Push(hex"0x83"), - JumpI, - Push(hex"0x00"), - Dup(1), - Revert, - JumpDest, - Pop, - Push(hex"0x8a"), - Push(hex"0xaa"), - Jump, - JumpDest, - Push(hex"0x40"), - MLoad, - Dup(1), - Dup(3), - Dup(2), - MStore, - Push(hex"0x20"), - Add, - Swap(2), - Pop, - Pop, - Push(hex"0x40"), - MLoad, - Dup(1), - Swap(2), - Sub, - Swap(1), - Return, - JumpDest, - Dup(1), - Push(hex"0x00"), - Dup(2), - Swap(1), - SStore, - Pop, - Pop, - Jump, - JumpDest, - Push(hex"0x00"), - Dup(1), - SLoad, - Swap(1), - Pop, - Swap(1), - Jump, - Stop - )) - } - } -} diff --git a/evm/src/test/scala/pravda/evm/parse/ParserTests.scala b/evm/src/test/scala/pravda/evm/parse/ParserTests.scala new file mode 100644 index 00000000..eb27e6c8 --- /dev/null +++ b/evm/src/test/scala/pravda/evm/parse/ParserTests.scala @@ -0,0 +1,718 @@ +package pravda.evm + +package parse + +import fastparse.byte.all._ +import pravda.evm._ +import pravda.evm.EVM._ +import utest._ + +object ParserTests extends TestSuite { + + val tests = Tests { + 'SimpleStorage - { + val bytes = readSolidityBinFile("SimpleStorage/SimpleStorage.bin") + + Parser(bytes) ==> + Right( + List( + Push(hex"0x80"), + Push(hex"0x40"), + MStore, + CallValue, + Dup(1), + IsZero, + Push(hex"0x0010"), + JumpI, + Push(hex"0x00"), + Dup(1), + Revert, + JumpDest, + Pop, + Push(hex"0xec"), + Dup(1), + Push(hex"0x001f"), + Push(hex"0x00"), + CodeCopy, + Push(hex"0x00"), + Return, + Invalid, + Push(hex"0x80"), + Push(hex"0x40"), + MStore, + Push(hex"0x04"), + CallDataSize, + Lt, + Push(hex"0x49"), + JumpI, + Push(hex"0x00"), + CallDataLoad, + Push(hex"0x0100000000000000000000000000000000000000000000000000000000"), + Swap(1), + Div, + Push(hex"0xffffffff"), + And, + Dup(1), + Push(hex"0x60fe47b1"), + Eq, + Push(hex"0x4e"), + JumpI, + Dup(1), + Push(hex"0x6d4ce63c"), + Eq, + Push(hex"0x85"), + JumpI, + JumpDest, + Push(hex"0x00"), + Dup(1), + Revert, + JumpDest, + CallValue, + Dup(1), + IsZero, + Push(hex"0x59"), + JumpI, + Push(hex"0x00"), + Dup(1), + Revert, + JumpDest, + Pop, + Push(hex"0x83"), + Push(hex"0x04"), + Dup(1), + CallDataSize, + Sub, + Push(hex"0x20"), + Dup(2), + Lt, + IsZero, + Push(hex"0x6e"), + JumpI, + Push(hex"0x00"), + Dup(1), + Revert, + JumpDest, + Dup(2), + Add, + Swap(1), + Dup(1), + Dup(1), + CallDataLoad, + Swap(1), + Push(hex"0x20"), + Add, + Swap(1), + Swap(3), + Swap(2), + Swap(1), + Pop, + Pop, + Pop, + Push(hex"0xad"), + Jump, + JumpDest, + Stop, + JumpDest, + CallValue, + Dup(1), + IsZero, + Push(hex"0x90"), + JumpI, + Push(hex"0x00"), + Dup(1), + Revert, + JumpDest, + Pop, + Push(hex"0x97"), + Push(hex"0xb7"), + Jump, + JumpDest, + Push(hex"0x40"), + MLoad, + Dup(1), + Dup(3), + Dup(2), + MStore, + Push(hex"0x20"), + Add, + Swap(2), + Pop, + Pop, + Push(hex"0x40"), + MLoad, + Dup(1), + Swap(2), + Sub, + Swap(1), + Return, + JumpDest, + Dup(1), + Push(hex"0x00"), + Dup(2), + Swap(1), + SStore, + Pop, + Pop, + Jump, + JumpDest, + Push(hex"0x00"), + Dup(1), + SLoad, + Swap(1), + Pop, + Swap(1), + Jump, + Invalid + )) + } + + 'SimpleToken - { + val bytes = readSolidityBinFile("SimpleToken/SimpleToken.bin") + + Parser(bytes) ==> Right( + List( + Push(hex"0x80"), + Push(hex"0x40"), + MStore, + CallValue, + Dup(1), + IsZero, + Push(hex"0x0010"), + JumpI, + Push(hex"0x00"), + Dup(1), + Revert, + JumpDest, + Pop, + Push(hex"0x0444"), + Dup(1), + Push(hex"0x0020"), + Push(hex"0x00"), + CodeCopy, + Push(hex"0x00"), + Return, + Invalid, + Push(hex"0x80"), + Push(hex"0x40"), + MStore, + Push(hex"0x04"), + CallDataSize, + Lt, + Push(hex"0x0062"), + JumpI, + Push(hex"0x00"), + CallDataLoad, + Push(hex"0x0100000000000000000000000000000000000000000000000000000000"), + Swap(1), + Div, + Push(hex"0xffffffff"), + And, + Dup(1), + Push(hex"0x27e235e3"), + Eq, + Push(hex"0x0067"), + JumpI, + Dup(1), + Push(hex"0x70a08231"), + Eq, + Push(hex"0x00cc"), + JumpI, + Dup(1), + Push(hex"0xa9059cbb"), + Eq, + Push(hex"0x0131"), + JumpI, + Dup(1), + Push(hex"0xf11b9fc8"), + Eq, + Push(hex"0x01a4"), + JumpI, + JumpDest, + Push(hex"0x00"), + Dup(1), + Revert, + JumpDest, + CallValue, + Dup(1), + IsZero, + Push(hex"0x0073"), + JumpI, + Push(hex"0x00"), + Dup(1), + Revert, + JumpDest, + Pop, + Push(hex"0x00b6"), + Push(hex"0x04"), + Dup(1), + CallDataSize, + Sub, + Push(hex"0x20"), + Dup(2), + Lt, + IsZero, + Push(hex"0x008a"), + JumpI, + Push(hex"0x00"), + Dup(1), + Revert, + JumpDest, + Dup(2), + Add, + Swap(1), + Dup(1), + Dup(1), + CallDataLoad, + Push(hex"0xffffffffffffffffffffffffffffffffffffffff"), + And, + Swap(1), + Push(hex"0x20"), + Add, + Swap(1), + Swap(3), + Swap(2), + Swap(1), + Pop, + Pop, + Pop, + Push(hex"0x0217"), + Jump, + JumpDest, + Push(hex"0x40"), + MLoad, + Dup(1), + Dup(3), + Dup(2), + MStore, + Push(hex"0x20"), + Add, + Swap(2), + Pop, + Pop, + Push(hex"0x40"), + MLoad, + Dup(1), + Swap(2), + Sub, + Swap(1), + Return, + JumpDest, + CallValue, + Dup(1), + IsZero, + Push(hex"0x00d8"), + JumpI, + Push(hex"0x00"), + Dup(1), + Revert, + JumpDest, + Pop, + Push(hex"0x011b"), + Push(hex"0x04"), + Dup(1), + CallDataSize, + Sub, + Push(hex"0x20"), + Dup(2), + Lt, + IsZero, + Push(hex"0x00ef"), + JumpI, + Push(hex"0x00"), + Dup(1), + Revert, + JumpDest, + Dup(2), + Add, + Swap(1), + Dup(1), + Dup(1), + CallDataLoad, + Push(hex"0xffffffffffffffffffffffffffffffffffffffff"), + And, + Swap(1), + Push(hex"0x20"), + Add, + Swap(1), + Swap(3), + Swap(2), + Swap(1), + Pop, + Pop, + Pop, + Push(hex"0x022f"), + Jump, + JumpDest, + Push(hex"0x40"), + MLoad, + Dup(1), + Dup(3), + Dup(2), + MStore, + Push(hex"0x20"), + Add, + Swap(2), + Pop, + Pop, + Push(hex"0x40"), + MLoad, + Dup(1), + Swap(2), + Sub, + Swap(1), + Return, + JumpDest, + CallValue, + Dup(1), + IsZero, + Push(hex"0x013d"), + JumpI, + Push(hex"0x00"), + Dup(1), + Revert, + JumpDest, + Pop, + Push(hex"0x018a"), + Push(hex"0x04"), + Dup(1), + CallDataSize, + Sub, + Push(hex"0x40"), + Dup(2), + Lt, + IsZero, + Push(hex"0x0154"), + JumpI, + Push(hex"0x00"), + Dup(1), + Revert, + JumpDest, + Dup(2), + Add, + Swap(1), + Dup(1), + Dup(1), + CallDataLoad, + Push(hex"0xffffffffffffffffffffffffffffffffffffffff"), + And, + Swap(1), + Push(hex"0x20"), + Add, + Swap(1), + Swap(3), + Swap(2), + Swap(1), + Dup(1), + CallDataLoad, + Swap(1), + Push(hex"0x20"), + Add, + Swap(1), + Swap(3), + Swap(2), + Swap(1), + Pop, + Pop, + Pop, + Push(hex"0x0277"), + Jump, + JumpDest, + Push(hex"0x40"), + MLoad, + Dup(1), + Dup(3), + IsZero, + IsZero, + IsZero, + IsZero, + Dup(2), + MStore, + Push(hex"0x20"), + Add, + Swap(2), + Pop, + Pop, + Push(hex"0x40"), + MLoad, + Dup(1), + Swap(2), + Sub, + Swap(1), + Return, + JumpDest, + CallValue, + Dup(1), + IsZero, + Push(hex"0x01b0"), + JumpI, + Push(hex"0x00"), + Dup(1), + Revert, + JumpDest, + Pop, + Push(hex"0x01fd"), + Push(hex"0x04"), + Dup(1), + CallDataSize, + Sub, + Push(hex"0x40"), + Dup(2), + Lt, + IsZero, + Push(hex"0x01c7"), + JumpI, + Push(hex"0x00"), + Dup(1), + Revert, + JumpDest, + Dup(2), + Add, + Swap(1), + Dup(1), + Dup(1), + CallDataLoad, + Push(hex"0xffffffffffffffffffffffffffffffffffffffff"), + And, + Swap(1), + Push(hex"0x20"), + Add, + Swap(1), + Swap(3), + Swap(2), + Swap(1), + Dup(1), + CallDataLoad, + Swap(1), + Push(hex"0x20"), + Add, + Swap(1), + Swap(3), + Swap(2), + Swap(1), + Pop, + Pop, + Pop, + Push(hex"0x0389"), + Jump, + JumpDest, + Push(hex"0x40"), + MLoad, + Dup(1), + Dup(3), + IsZero, + IsZero, + IsZero, + IsZero, + Dup(2), + MStore, + Push(hex"0x20"), + Add, + Swap(2), + Pop, + Pop, + Push(hex"0x40"), + MLoad, + Dup(1), + Swap(2), + Sub, + Swap(1), + Return, + JumpDest, + Push(hex"0x00"), + Push(hex"0x20"), + MStore, + Dup(1), + Push(hex"0x00"), + MStore, + Push(hex"0x40"), + Push(hex"0x00"), + Sha3, + Push(hex"0x00"), + Swap(2), + Pop, + Swap(1), + Pop, + SLoad, + Dup(2), + Jump, + JumpDest, + Push(hex"0x00"), + Dup(1), + Push(hex"0x00"), + Dup(4), + Push(hex"0xffffffffffffffffffffffffffffffffffffffff"), + And, + Push(hex"0xffffffffffffffffffffffffffffffffffffffff"), + And, + Dup(2), + MStore, + Push(hex"0x20"), + Add, + Swap(1), + Dup(2), + MStore, + Push(hex"0x20"), + Add, + Push(hex"0x00"), + Sha3, + SLoad, + Swap(1), + Pop, + Swap(2), + Swap(1), + Pop, + Jump, + JumpDest, + Push(hex"0x00"), + Dup(2), + Push(hex"0x00"), + Dup(1), + Caller, + Push(hex"0xffffffffffffffffffffffffffffffffffffffff"), + And, + Push(hex"0xffffffffffffffffffffffffffffffffffffffff"), + And, + Dup(2), + MStore, + Push(hex"0x20"), + Add, + Swap(1), + Dup(2), + MStore, + Push(hex"0x20"), + Add, + Push(hex"0x00"), + Sha3, + SLoad, + Sub, + Push(hex"0x00"), + Dup(1), + Caller, + Push(hex"0xffffffffffffffffffffffffffffffffffffffff"), + And, + Push(hex"0xffffffffffffffffffffffffffffffffffffffff"), + And, + Dup(2), + MStore, + Push(hex"0x20"), + Add, + Swap(1), + Dup(2), + MStore, + Push(hex"0x20"), + Add, + Push(hex"0x00"), + Sha3, + Dup(2), + Swap(1), + SStore, + Pop, + Dup(2), + Push(hex"0x00"), + Dup(1), + Dup(6), + Push(hex"0xffffffffffffffffffffffffffffffffffffffff"), + And, + Push(hex"0xffffffffffffffffffffffffffffffffffffffff"), + And, + Dup(2), + MStore, + Push(hex"0x20"), + Add, + Swap(1), + Dup(2), + MStore, + Push(hex"0x20"), + Add, + Push(hex"0x00"), + Sha3, + SLoad, + Add, + Push(hex"0x00"), + Dup(1), + Dup(6), + Push(hex"0xffffffffffffffffffffffffffffffffffffffff"), + And, + Push(hex"0xffffffffffffffffffffffffffffffffffffffff"), + And, + Dup(2), + MStore, + Push(hex"0x20"), + Add, + Swap(1), + Dup(2), + MStore, + Push(hex"0x20"), + Add, + Push(hex"0x00"), + Sha3, + Dup(2), + Swap(1), + SStore, + Pop, + Push(hex"0x01"), + Swap(1), + Pop, + Swap(3), + Swap(2), + Pop, + Pop, + Jump, + JumpDest, + Push(hex"0x00"), + Dup(2), + Push(hex"0x00"), + Dup(1), + Dup(6), + Push(hex"0xffffffffffffffffffffffffffffffffffffffff"), + And, + Push(hex"0xffffffffffffffffffffffffffffffffffffffff"), + And, + Dup(2), + MStore, + Push(hex"0x20"), + Add, + Swap(1), + Dup(2), + MStore, + Push(hex"0x20"), + Add, + Push(hex"0x00"), + Sha3, + SLoad, + Add, + Push(hex"0x00"), + Dup(1), + Dup(6), + Push(hex"0xffffffffffffffffffffffffffffffffffffffff"), + And, + Push(hex"0xffffffffffffffffffffffffffffffffffffffff"), + And, + Dup(2), + MStore, + Push(hex"0x20"), + Add, + Swap(1), + Dup(2), + MStore, + Push(hex"0x20"), + Add, + Push(hex"0x00"), + Sha3, + Dup(2), + Swap(1), + SStore, + Pop, + Push(hex"0x01"), + Swap(1), + Pop, + Swap(3), + Swap(2), + Pop, + Pop, + Jump, + Invalid + )) + } + } +} diff --git a/evm/src/test/scala/pravda/evm/translate/opcode/EvaluateTests.scala b/evm/src/test/scala/pravda/evm/translate/opcode/EvaluateTests.scala deleted file mode 100644 index 26633b10..00000000 --- a/evm/src/test/scala/pravda/evm/translate/opcode/EvaluateTests.scala +++ /dev/null @@ -1,234 +0,0 @@ -package pravda.evm.translate.opcode - -import fastparse.byte.all._ -import pravda.evm._ -import pravda.evm.EVM._ -import pravda.evm.translate.Translator -import pravda.vm.Data.Primitive.BigInt -import pravda.vm.VmSandbox.EnvironmentEffect.{StorageGet, StoragePut} -import pravda.vm.VmSandbox.Preconditions -import utest._ - -import scala.collection.mutable.ArrayBuffer - -object EvaluateTests extends TestSuite { - - val tests = Tests { - //FIXME when memory usage will be fixed. All tests will be incorrect - import SimpleTranslation._ - import pravda.vm.Data.Primitive._ - val precondition = Preconditions(balances = Map.empty, watts = 1000L, executor = None) - "PUSH" - { - run(evmOpToOps(Push(hex"0x80")), precondition) ==> Right( - expectations(101L, stack = ArrayBuffer(BigInt(scala.BigInt(128))))) - } - - "DUP" - { - run(Translator(List(Push(hex"0x80"), Dup(1))), precondition) ==> - Right(expectations(102L, stack = ArrayBuffer(BigInt(scala.BigInt(128)), BigInt(scala.BigInt(128))))) - - run(Translator(List(Push(hex"0x80"), Push(hex"0x60"), Dup(2))), precondition) ==> - Right( - expectations(104L, - stack = - ArrayBuffer(BigInt(scala.BigInt(128)), BigInt(scala.BigInt(96)), BigInt(scala.BigInt(128))))) - } - - "SWAP" - { - run(Translator(List(Push(hex"0x80"), Push(hex"0x60"), Swap(1))), precondition) ==> - Right(expectations(103L, stack = ArrayBuffer(BigInt(scala.BigInt(96)), BigInt(scala.BigInt(128))))) - - run(Translator(List(Push(hex"0x80"), Push(hex"0x60"), Push(hex"0x40"), Swap(2))), precondition) ==> - Right( - expectations(105L, - stack = - ArrayBuffer(BigInt(scala.BigInt(64)), BigInt(scala.BigInt(96)), BigInt(scala.BigInt(128))))) - } - - //TODO tests with overflow - - "ADD" - { - run(Translator(List(Push(hex"0x80"), Push(hex"0x60"), Add)), precondition) ==> - Right(expectations(113L, stack = ArrayBuffer(BigInt(scala.BigInt(224))))) - } - - "MUL" - { - run(Translator(List(Push(hex"0x80"), Push(hex"0x60"), Mul)), precondition) ==> - Right(expectations(113L, stack = ArrayBuffer(BigInt(scala.BigInt(12288))))) - } - - "DIV" - { - run(Translator(List(Push(hex"0x2"), Push(hex"0x60"), Div)), precondition) ==> - Right(expectations(113L, stack = ArrayBuffer(BigInt(scala.BigInt(48))))) - } - - "MOD" - { - run(Translator(List(Push(hex"0x2"), Push(hex"0x60"), Mod)), precondition) ==> - Right(expectations(113L, stack = ArrayBuffer(BigInt(scala.BigInt(0))))) - - run(Translator(List(Push(hex"0x7"), Push(hex"0x60"), Mod)), precondition) ==> - Right(expectations(113L, stack = ArrayBuffer(BigInt(scala.BigInt(5))))) - } - - "SUB" - { - run(Translator(List(Push(hex"0x60"), Push(hex"0x80"), Sub)), precondition) ==> - Right(expectations(126L, stack = ArrayBuffer(BigInt(scala.BigInt(32))))) - } - - "ADDMOD" - { - run(Translator(List(Push(hex"0x3"), Push(hex"0x80"), Push(hex"0x60"), AddMod)), precondition) ==> - Right(expectations(154L, stack = ArrayBuffer(BigInt(scala.BigInt(2))))) - } - - "MULMOD" - { - run(Translator(List(Push(hex"0x5"), Push(hex"0x80"), Push(hex"0x60"), MulMod)), precondition) ==> - Right(expectations(154L, stack = ArrayBuffer(BigInt(scala.BigInt(3))))) - } - - "OR" - { - val x = hex"0x01" - val y = hex"0x02" - val expectation = scala.BigInt(1, x.toArray) | scala.BigInt(1, y.toArray) - run(Translator(List(Push(x), Push(y), Or)), precondition) ==> - Right(expectations(108L, stack = ArrayBuffer(BigInt(expectation)))) - - run(Translator(List(Push(hex"0x1"), Push(hex"0x3"), Or)), precondition) ==> - Right(expectations(108L, stack = ArrayBuffer(BigInt(scala.BigInt(3))))) - } - - "AND" - { - val x = hex"0x01" - val y = hex"0x02" - val expectation = scala.BigInt(1, x.toArray) & scala.BigInt(1, y.toArray) - - run(Translator(List(Push(x), Push(y), And)), precondition) ==> - Right(expectations(108L, stack = ArrayBuffer(BigInt(expectation)))) - run(Translator(List(Push(hex"0x1"), Push(hex"0x3"), And)), precondition) ==> - Right(expectations(108L, stack = ArrayBuffer(BigInt(scala.BigInt(1))))) - } - - "XOR" - { - val x = hex"0x426" - val y = hex"0x284" - val expectation = scala.BigInt(1, x.toArray) ^ scala.BigInt(1, y.toArray) - - run(Translator(List(Push(x), Push(y), Xor)), precondition) ==> - Right(expectations(108L, stack = ArrayBuffer(BigInt(expectation)))) - } - - "BYTE" - { - val x = hex"0x426" - var pos = hex"0x1f" - - run(Translator(List(Push(x), Push(pos), Byte)), precondition) ==> - Right(expectations(191L, stack = ArrayBuffer(BigInt(scala.BigInt(x.last.toInt))))) - - pos = hex"0x1e" - val expectation = x.reverse.tail.head.toInt - run(Translator(List(Push(x), Push(pos), Byte)), precondition) ==> - Right(expectations(191L, stack = ArrayBuffer(BigInt(scala.BigInt(expectation))))) - } - - "LT" - { - val x = hex"0x4" - val y = hex"0x2" - run(Translator(List(Push(x), Push(y), Lt)), precondition) ==> - Right(expectations(120L, stack = ArrayBuffer(BigInt(scala.BigInt(1))))) - - run(Translator(List(Push(y), Push(x), Lt)), precondition) ==> - Right(expectations(120L, stack = ArrayBuffer(BigInt(scala.BigInt(0))))) - } - - "GT" - { - val x = hex"0x4" - val y = hex"0x2" - run(Translator(List(Push(x), Push(y), Gt)), precondition) ==> - Right(expectations(120L, stack = ArrayBuffer(BigInt(scala.BigInt(0))))) - - run(Translator(List(Push(y), Push(x), Gt)), precondition) ==> - Right(expectations(120L, stack = ArrayBuffer(BigInt(scala.BigInt(1))))) - } - - "EQ" - { - val x = hex"0x4" - val y = hex"0x2" - run(Translator(List(Push(x), Push(y), Eq)), precondition) ==> - Right(expectations(120L, stack = ArrayBuffer(BigInt(scala.BigInt(0))))) - - run(Translator(List(Push(x), Push(x), Eq)), precondition) ==> - Right(expectations(120L, stack = ArrayBuffer(BigInt(scala.BigInt(1))))) - } - - val `0` = hex"0x0" - val `1` = hex"0x1" - val `3` = hex"0x3" - val `4` = hex"0x4" - "JUMPS" - { - - run( - Translator.translateActualContract( - List( - 0 -> Push(`4`), - 2 -> Push(`0`), - 3 -> CodeCopy, - 4 -> Push(`4`), - 4 -> Push(`3`), - 5 -> Jump, - 5 -> Push(`4`), - 6 -> Push(`4`), - 7 -> Push(`4`), - 7 -> JumpDest, - )), - precondition - ).foreach({ expect => - expect.watts ==> 154L - expect.memory.stack ==> ArrayBuffer(BigInt(scala.BigInt(4))) - expect.error ==> None - }) - - run( - Translator.translateActualContract( - List( - 0 -> Push(`4`), - 2 -> Push(`0`), - 3 -> CodeCopy, - 4 -> Push(`4`), - 5 -> Push(`1`), - 6 -> Push(`3`), - 5 -> JumpI, - 5 -> Push(`4`), - 6 -> Push(`4`), - 7 -> Push(`4`), - 7 -> JumpDest, - )), - precondition - ).foreach({ expect => - expect.watts ==> 173L - expect.memory.stack ==> ArrayBuffer(BigInt(scala.BigInt(4))) - expect.error ==> None - }) - } - - "SSTORE SLOAD" - { - - run(Translator( - List( - Push(`4`), - Push(`3`), - SStore, - Push(`3`), - SLoad - ) - ), - precondition).foreach({ expect => - expect.watts ==> 193 - expect.effects ==> ArrayBuffer(StoragePut(BigInt(scala.BigInt(3)), BigInt(scala.BigInt(4))), - StorageGet(BigInt(scala.BigInt(3)), Some(BigInt(scala.BigInt(4))))) - expect.memory.stack ==> ArrayBuffer(BigInt(scala.BigInt(4))) - expect.error ==> None - }) - } - - } - -} diff --git a/evm/src/test/scala/pravda/evm/translate/opcode/FullContractTests.scala b/evm/src/test/scala/pravda/evm/translate/opcode/FullContractTests.scala new file mode 100644 index 00000000..386f35e3 --- /dev/null +++ b/evm/src/test/scala/pravda/evm/translate/opcode/FullContractTests.scala @@ -0,0 +1,342 @@ +package pravda.evm + +package translate.opcode + +import com.google.protobuf.ByteString +import pravda.evm.abi.parse.AbiParser +import pravda.evm.parse.Parser +import pravda.evm.utils._ +import pravda.vm.Data.Primitive._ +import pravda.vm.{Data, VmSandbox} +import pravda.common.domain.Address +import pravda.vm.Data.Array.Int8Array +import pravda.vm.Effect.{StorageRead, StorageWrite} +import utest._ +import pravda.common.bytes.hex._ + +import scala.collection.mutable.ArrayBuffer + +object FullContractTests extends TestSuite { + import VmSandbox.{ExpectationsWithoutWatts => Expectations} + + val tests = Tests { + 'SimpleStorageGet - { + val preconditions = VmSandbox.Preconditions(`watts-limit` = 10000L, + stack = Seq(Data.Primitive.Utf8("get")), + storage = Map(evmWord(Array(0)) -> evmWord(Array(1)))) + + val Right(ops) = Parser.parseWithIndices(readSolidityBinFile("SimpleStorage/SimpleStorage.bin")) + val Right(abi) = AbiParser.parseAbi(readSolidityABI("SimpleStorage/SimpleStorage.abi")) + + EvmSandbox.runAddressedCode(preconditions, ops, abi) ==> Right( + Expectations( + stack = Seq(BigInt(scala.BigInt(1))), + heap = Map(Ref(0) -> Int8Array(ArrayBuffer(1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0))), + ArrayBuffer( + StorageRead( + Address @@ ByteString.copyFrom(Array.fill[Byte](32)(0)), + evmWord(Array(0)), + Some(evmWord(Array(1))) + ), + StorageRead( + Address @@ ByteString.copyFrom(Array.fill[Byte](32)(0)), + evmWord(Array(0)), + Some(evmWord(Array(1))) + ), + ) + )) + } + + 'SimpleStorageSet - { + val preconditions = VmSandbox.Preconditions( + `watts-limit` = 10000L, + stack = Seq(Data.Primitive.Int64(10), Data.Primitive.Utf8("set")), + storage = Map() + ) + + val Right(ops) = Parser.parseWithIndices(readSolidityBinFile("SimpleStorage/SimpleStorage.bin")) + val Right(abi) = AbiParser.parseAbi(readSolidityABI("SimpleStorage/SimpleStorage.abi")) + + EvmSandbox.runAddressedCode(preconditions, ops, abi) ==> Right( + Expectations( + stack = Seq(), + heap = Map(Ref(0) -> Int8Array(ArrayBuffer(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0))), + ArrayBuffer( + StorageWrite( + Address @@ ByteString.copyFrom(Array.fill[Byte](32)(0)), + evmWord(Array(0)), + None, + evmWord(Array(10)) + )), + None + )) + } + + 'SimpleTokenEmit - { + val preconditions = VmSandbox.Preconditions( + `watts-limit` = 10000L, + stack = Seq(Data.Primitive.Bytes(ByteString.copyFrom((1 to 32).toArray.map(_.toByte))), + Data.Primitive.Int64(10), + Data.Primitive.Utf8("emitTokens")), + storage = Map() + ) + + val Right(ops) = Parser.parseWithIndices(readSolidityBinFile("SimpleToken/SimpleToken.bin")) + val Right(abi) = AbiParser.parseAbi(readSolidityABI("SimpleToken/SimpleToken.abi")) + + EvmSandbox.runAddressedCode(preconditions, ops, abi) ==> Right( + Expectations( + stack = Seq(Bool.True), + heap = Map(Ref(0) -> Int8Array(ArrayBuffer(1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0))), + ArrayBuffer( + StorageRead( + Address @@ ByteString.copyFrom(Array.fill[Byte](32)(0)), + Bytes(ByteString.copyFrom(hex"1e1e7f10bf6e615fa5a4e56786997be589a0a77fc0b37a4c3fdec08afbe4ed8c")), + None + ), + StorageWrite( + Address @@ ByteString.copyFrom(Array.fill[Byte](32)(0)), + Bytes(ByteString.copyFrom(hex"1e1e7f10bf6e615fa5a4e56786997be589a0a77fc0b37a4c3fdec08afbe4ed8c")), + None, + Bytes(ByteString.copyFrom(hex"0a00000000000000000000000000000000000000000000000000000000000000")) + ) + ), + None + )) + } + + 'SimpleTokenBalance - { + val preconditions = VmSandbox.Preconditions( + `watts-limit` = 10000L, + stack = Seq( + Data.Primitive.Bytes(ByteString.copyFrom((1 to 32).toArray.map(_.toByte))), + Data.Primitive.Utf8("balanceOf") + ), + storage = Map( + Bytes(ByteString.copyFrom(hex"1e1e7f10bf6e615fa5a4e56786997be589a0a77fc0b37a4c3fdec08afbe4ed8c")) -> + Bytes(ByteString.copyFrom(hex"0a00000000000000000000000000000000000000000000000000000000000000"))) + ) + + val Right(ops) = Parser.parseWithIndices(readSolidityBinFile("SimpleToken/SimpleToken.bin")) + val Right(abi) = AbiParser.parseAbi(readSolidityABI("SimpleToken/SimpleToken.abi")) + + EvmSandbox.runAddressedCode(preconditions, ops, abi) ==> Right( + Expectations( + stack = Seq(BigInt(scala.BigInt(10))), + heap = Map(Ref(0) -> Int8Array(ArrayBuffer(10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0))), + ArrayBuffer( + StorageRead( + Address @@ ByteString.copyFrom(Array.fill[Byte](32)(0)), + Bytes(ByteString.copyFrom(hex"1e1e7f10bf6e615fa5a4e56786997be589a0a77fc0b37a4c3fdec08afbe4ed8c")), + Some(Bytes(ByteString.copyFrom(hex"0a00000000000000000000000000000000000000000000000000000000000000"))) + ), + StorageRead( + Address @@ ByteString.copyFrom(Array.fill[Byte](32)(0)), + Bytes(ByteString.copyFrom(hex"1e1e7f10bf6e615fa5a4e56786997be589a0a77fc0b37a4c3fdec08afbe4ed8c")), + Some(Bytes(ByteString.copyFrom(hex"0a00000000000000000000000000000000000000000000000000000000000000"))) + ) + ) + )) + } + + 'SimpleTokenTransfer - { + val preconditions = VmSandbox.Preconditions( + `watts-limit` = 10000L, + stack = Seq( + Data.Primitive.Bytes(ByteString.copyFrom((2 to 33).toArray.map(_.toByte))), + Data.Primitive.Int64(2), + Data.Primitive.Utf8("transfer") + ), + storage = Map( + Bytes(ByteString.copyFrom(hex"1e1e7f10bf6e615fa5a4e56786997be589a0a77fc0b37a4c3fdec08afbe4ed8c")) -> + Bytes(ByteString.copyFrom(hex"0a00000000000000000000000000000000000000000000000000000000000000")), + Bytes(ByteString.copyFrom(hex"3b92ad5fdb196e1ab7df6126be9a99dcd51fcd55e21de9fc19c40e3951836661")) -> + Bytes(ByteString.copyFrom(hex"0b00000000000000000000000000000000000000000000000000000000000000")) + ) + ) + + val Right(ops) = Parser.parseWithIndices(readSolidityBinFile("SimpleToken/SimpleToken.bin")) + val Right(abi) = AbiParser.parseAbi(readSolidityABI("SimpleToken/SimpleToken.abi")) + + EvmSandbox.runAddressedCode(preconditions, ops, abi) ==> Right( + Expectations( + stack = Seq(Bool.True), + heap = Map(Ref(0) -> Int8Array(ArrayBuffer(1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0))), + ArrayBuffer( + StorageRead( + Address @@ ByteString.copyFrom(Array.fill[Byte](32)(0)), + Bytes(ByteString.copyFrom(hex"1e1e7f10bf6e615fa5a4e56786997be589a0a77fc0b37a4c3fdec08afbe4ed8c")), + Some(Bytes(ByteString.copyFrom(hex"0a00000000000000000000000000000000000000000000000000000000000000"))) + ), + StorageRead( + Address @@ ByteString.copyFrom(Array.fill[Byte](32)(0)), + Bytes(ByteString.copyFrom(hex"1e1e7f10bf6e615fa5a4e56786997be589a0a77fc0b37a4c3fdec08afbe4ed8c")), + Some(Bytes(ByteString.copyFrom(hex"0a00000000000000000000000000000000000000000000000000000000000000"))) + ), + StorageWrite( + Address @@ ByteString.copyFrom(Array.fill[Byte](32)(0)), + Bytes(ByteString.copyFrom(hex"1e1e7f10bf6e615fa5a4e56786997be589a0a77fc0b37a4c3fdec08afbe4ed8c")), + Some(Bytes(ByteString.copyFrom(hex"0a00000000000000000000000000000000000000000000000000000000000000"))), + Bytes(ByteString.copyFrom(hex"0800000000000000000000000000000000000000000000000000000000000000")) + ), + StorageRead( + Address @@ ByteString.copyFrom(Array.fill[Byte](32)(0)), + Bytes(ByteString.copyFrom(hex"3b92ad5fdb196e1ab7df6126be9a99dcd51fcd55e21de9fc19c40e3951836661")), + Some(Bytes(ByteString.copyFrom(hex"0b00000000000000000000000000000000000000000000000000000000000000"))) + ), + StorageRead( + Address @@ ByteString.copyFrom(Array.fill[Byte](32)(0)), + Bytes(ByteString.copyFrom(hex"3b92ad5fdb196e1ab7df6126be9a99dcd51fcd55e21de9fc19c40e3951836661")), + Some(Bytes(ByteString.copyFrom(hex"0b00000000000000000000000000000000000000000000000000000000000000"))) + ), + StorageWrite( + Address @@ ByteString.copyFrom(Array.fill[Byte](32)(0)), + Bytes(ByteString.copyFrom(hex"3b92ad5fdb196e1ab7df6126be9a99dcd51fcd55e21de9fc19c40e3951836661")), + Some(Bytes(ByteString.copyFrom(hex"0b00000000000000000000000000000000000000000000000000000000000000"))), + Bytes(ByteString.copyFrom(hex"0d00000000000000000000000000000000000000000000000000000000000000")) + ) + ) + )) + } + } +} diff --git a/evm/src/test/scala/pravda/evm/translate/opcode/RunTests.scala b/evm/src/test/scala/pravda/evm/translate/opcode/RunTests.scala new file mode 100644 index 00000000..6f42c4c0 --- /dev/null +++ b/evm/src/test/scala/pravda/evm/translate/opcode/RunTests.scala @@ -0,0 +1,121 @@ +package pravda.evm.translate.opcode + +import fastparse.byte.all._ +import pravda.evm.EVM._ +import pravda.evm.EvmSandbox +import pravda.evm.abi.parse.AbiParser.AbiFunction +import pravda.evm.utils._ +import pravda.vm.VmSandbox +import utest._ + +object RunTests extends TestSuite { + + import VmSandbox.{ExpectationsWithoutWatts => Expectations} + + val tests = Tests { + + val preconditions = VmSandbox.Preconditions(`watts-limit` = 10000L) + val abi = List(AbiFunction(true, "", Nil, Nil, true, "", None)) + + "DUP" - { + EvmSandbox.runCode(preconditions, List(Push(hex"0x01"), Dup(1)), abi) ==> + Right(Expectations(stack = Seq(evmWord(Array(0x01)), evmWord(Array(0x01))))) + + EvmSandbox.runCode(preconditions, List(Push(hex"0x01"), Push(hex"0x02"), Dup(2)), abi) ==> + Right(Expectations(stack = Seq(evmWord(Array(0x01)), evmWord(Array(0x02)), evmWord(Array(0x01))))) + } + + "SWAP" - { + EvmSandbox.runCode(preconditions, List(Push(hex"0x01"), Push(hex"0x02"), Swap(1)), abi) ==> + Right(Expectations(stack = Seq(evmWord(Array(0x02)), evmWord(Array(0x01))))) + + EvmSandbox.runCode(preconditions, List(Push(hex"0x01"), Push(hex"0x02"), Push(hex"0x03"), Swap(2)), abi) ==> + Right(Expectations(stack = Seq(evmWord(Array(0x03)), evmWord(Array(0x02)), evmWord(Array(0x01))))) + } + + //TODO tests with overflow + + "ADD" - { + EvmSandbox.runCode(preconditions, List(Push(hex"0x01"), Push(hex"0x02"), Add), abi) ==> + Right(Expectations(stack = Seq(evmWord(Array(0x03))))) + } + + "MUL" - { + EvmSandbox.runCode(preconditions, List(Push(hex"0x02"), Push(hex"0x03"), Mul), abi) ==> + Right(Expectations(stack = Seq(evmWord(Array(0x06))))) + } + + "DIV" - { + EvmSandbox.runCode(preconditions, List(Push(hex"0x02"), Push(hex"0x06"), Div), abi) ==> + Right(Expectations(stack = Seq(evmWord(Array(0x03))))) + } + + "MOD" - { + EvmSandbox.runCode(preconditions, List(Push(hex"0x02"), Push(hex"0x05"), Mod), abi) ==> + Right(Expectations(stack = Seq(evmWord(Array(0x01))))) + } + + "SUB" - { + EvmSandbox.runCode(preconditions, List(Push(hex"0x02"), Push(hex"0x05"), Sub), abi) ==> + Right(Expectations(stack = Seq(evmWord(Array(0x03))))) + } + + "OR" - { + EvmSandbox.runCode(preconditions, List(Push(hex"0x05"), Push(hex"0x03"), Or), abi) ==> + Right(Expectations(stack = Seq(evmWord(Array(0x07))))) + } + + "AND" - { + EvmSandbox.runCode(preconditions, List(Push(hex"0x05"), Push(hex"0x03"), And), abi) ==> + Right(Expectations(stack = Seq(evmWord(Array(0x01))))) + } + + "XOR" - { + EvmSandbox.runCode(preconditions, List(Push(hex"0x05"), Push(hex"0x03"), Xor), abi) ==> + Right(Expectations(stack = Seq(evmWord(Array(0x06))))) + } + +// "BYTE" - { +// val x = hex"0x426" +// var pos = hex"0x1f" +// +// EvmSandbox.runCode(preconditions, List(Push(x), Push(pos), Byte), abi) ==> +// Right(Expectations(stack = Seq(BigInt(scala.BigInt(x.last.toInt))))) +// +// pos = hex"0x1e" +// val expectation = x.reverse.tail.head.toInt +// EvmSandbox.runCode(preconditions, List(Push(x), Push(pos), Byte), abi) ==> +// Right(Expectations(stack = Seq(BigInt(scala.BigInt(expectation))))) +// } + + "LT" - { + val x = hex"0x4" + val y = hex"0x2" + EvmSandbox.runCode(preconditions, List(Push(x), Push(y), Lt), abi) ==> + Right(Expectations(stack = Seq(evmWord(Array(1))))) + + EvmSandbox.runCode(preconditions, List(Push(y), Push(x), Lt), abi) ==> + Right(Expectations(stack = Seq(evmWord(Array(0))))) + } + + "GT" - { + val x = hex"0x4" + val y = hex"0x2" + EvmSandbox.runCode(preconditions, List(Push(x), Push(y), Gt), abi) ==> + Right(Expectations(stack = Seq(evmWord(Array(0))))) + + EvmSandbox.runCode(preconditions, List(Push(y), Push(x), Gt), abi) ==> + Right(Expectations(stack = Seq(evmWord(Array(1))))) + } + + "EQ" - { + val x = hex"0x4" + val y = hex"0x2" + EvmSandbox.runCode(preconditions, List(Push(x), Push(y), Eq), abi) ==> + Right(Expectations(stack = Seq(evmWord(Array(0))))) + + EvmSandbox.runCode(preconditions, List(Push(x), Push(x), Eq), abi) ==> + Right(Expectations(stack = Seq(evmWord(Array(1))))) + } + } +} diff --git a/evm/src/test/scala/pravda/evm/translate/opcode/TranslateTests.scala b/evm/src/test/scala/pravda/evm/translate/opcode/TranslateTests.scala index 96825e15..4acf89ba 100644 --- a/evm/src/test/scala/pravda/evm/translate/opcode/TranslateTests.scala +++ b/evm/src/test/scala/pravda/evm/translate/opcode/TranslateTests.scala @@ -2,8 +2,13 @@ package pravda.evm.translate.opcode import fastparse.byte.all._ import pravda.evm.EVM._ +import pravda.evm.abi.parse.AbiParser +import pravda.evm.parse.Parser +import pravda.evm.{readSolidityABI, readSolidityBinFile} +import pravda.evm.translate.Translator import pravda.vm.Opcodes -import pravda.vm.asm.Operation +import pravda.vm.asm.{Operation, PravdaAssembler} +import pravda.evm.utils._ import utest._ object TranslateTests extends TestSuite { @@ -11,19 +16,2078 @@ object TranslateTests extends TestSuite { val tests = Tests { import SimpleTranslation._ - "PUSH" - { - evmOpToOps(Push(hex"0x80")) ==> Right(List(pushBigInt(BigInt(128)))) - } - "DUP" - { - evmOpToOps(Dup(1)) ==> Right(List(Operation(Opcodes.DUP))) - evmOpToOps(Dup(2)) ==> Right(List(pushInt(2), Operation(Opcodes.DUPN))) + 'Basic - { + "push" - { + evmOpToOps(Push(hex"0x80")) ==> Right(List(Operation.Push(evmWord(Array(-128))))) + } + + "dup" - { + evmOpToOps(Dup(1)) ==> Right(List(Operation(Opcodes.DUP))) + evmOpToOps(Dup(2)) ==> Right(List(pushInt(2), Operation(Opcodes.DUPN))) + } + + "swap" - { + evmOpToOps(Swap(1)) ==> Right(List(Operation(Opcodes.SWAP))) + evmOpToOps(Swap(2)) ==> Right(List(pushInt(3), Operation(Opcodes.SWAPN))) + } } - "SWAP" - { - evmOpToOps(Swap(1)) ==> Right(List(Operation(Opcodes.SWAP))) - evmOpToOps(Swap(2)) ==> Right(List(pushInt(3), Operation(Opcodes.SWAPN))) + 'Contracts - { + + 'SimpleStorage - { + val Right(ops) = Parser.parseWithIndices(readSolidityBinFile("SimpleStorage/SimpleStorage.bin")) + val Right(abi) = AbiParser.parseAbi(readSolidityABI("SimpleStorage/SimpleStorage.abi")) + val Right(asm) = Translator.translateActualContract(ops, abi) + + PravdaAssembler.render(asm) ==> + """@__start_evm_program: + |push int32(1024) + |push int8(1) + |new_array + |swap + |dup + |push "set" + |eq + |not + |jumpi @not_set + |push int32(3) + |swapn + |swap + |push int32(2) + |swapn + |push x + |swap + |push int8(14) + |cast + |push int32(8) + |scall + |concat + |push x00000000 + |concat + |swap + |push null + |jump @_lbl_78 + |@not_set: + |dup + |push "get" + |eq + |not + |jumpi @not_get + |swap + |push x + |push x00000000 + |concat + |swap + |push null + |jump @_lbl_133 + |@not_get: + |@_lbl_73: + |push x0000000000000000000000000000000000000000000000000000000000000000 + |dup + |push "Revert" + |throw + |@_lbl_78: + |@_lbl_89: + |pop + |push x8300000000000000000000000000000000000000000000000000000000000000 + |push x0400000000000000000000000000000000000000000000000000000000000000 + |dup + |push int32(5) + |dupn + |length + |push int8(4) + |cast + |swap + |push int8(4) + |cast + |swap + |swap + |push bigint(-1) + |mul + |add + |push int8(14) + |cast + |push int8(8) + |scall + |push x2000000000000000000000000000000000000000000000000000000000000000 + |push int32(2) + |dupn + |push int8(4) + |cast + |swap + |push int8(4) + |cast + |swap + |lt + |push int8(14) + |cast + |push int8(8) + |scall + |push x0000000000000000000000000000000000000000000000000000000000000000 + |eq + |push int8(14) + |cast + |push int8(8) + |scall + |push x6E00000000000000000000000000000000000000000000000000000000000000 + |pop + |push int8(9) + |cast + |jumpi @_lbl_110 + |push x0000000000000000000000000000000000000000000000000000000000000000 + |dup + |push "Revert" + |throw + |@_lbl_110: + |push int32(2) + |dupn + |push int8(4) + |cast + |swap + |push int8(4) + |cast + |swap + |add + |push int8(14) + |cast + |push int8(8) + |scall + |swap + |dup + |dup + |push int8(4) + |cast + |push int32(7) + |dupn + |swap + |dup + |push int32(32) + |add + |swap + |slice + |swap + |push x2000000000000000000000000000000000000000000000000000000000000000 + |push int8(4) + |cast + |swap + |push int8(4) + |cast + |swap + |add + |push int8(14) + |cast + |push int8(8) + |scall + |swap + |push int32(4) + |swapn + |push int32(3) + |swapn + |swap + |pop + |pop + |pop + |push xAD00000000000000000000000000000000000000000000000000000000000000 + |pop + |jump @_lbl_173 + |@_lbl_131: + |pop + |pop + |pop + |stop + |@_lbl_133: + |@_lbl_144: + |pop + |push x9700000000000000000000000000000000000000000000000000000000000000 + |push xB700000000000000000000000000000000000000000000000000000000000000 + |pop + |jump @_lbl_183 + |@_lbl_151: + |push x4000000000000000000000000000000000000000000000000000000000000000 + |push bigint(32) + |swap + |push int8(4) + |cast + |push int32(4) + |dupn + |push int8(6) + |scall + |dup + |push int32(3) + |dupn + |push int32(2) + |dupn + |swap + |push int8(8) + |scall + |swap + |push int8(4) + |cast + |push int32(6) + |dupn + |push int8(7) + |scall + |push int32(5) + |swapn + |pop + |push x2000000000000000000000000000000000000000000000000000000000000000 + |push int8(4) + |cast + |swap + |push int8(4) + |cast + |swap + |add + |push int8(14) + |cast + |push int8(8) + |scall + |push int32(3) + |swapn + |pop + |pop + |push x4000000000000000000000000000000000000000000000000000000000000000 + |push bigint(32) + |swap + |push int8(4) + |cast + |push int32(4) + |dupn + |push int8(6) + |scall + |dup + |push int32(3) + |swapn + |push int8(4) + |cast + |swap + |push int8(4) + |cast + |swap + |swap + |push bigint(-1) + |mul + |add + |push int8(14) + |cast + |push int8(8) + |scall + |swap + |push int8(4) + |cast + |swap + |push int8(4) + |cast + |swap + |push int32(3) + |dupn + |push int8(6) + |scall + |swap + |pop + |swap + |pop + |swap + |jump @convert_result + |@_lbl_173: + |dup + |push x0000000000000000000000000000000000000000000000000000000000000000 + |push int32(2) + |dupn + |swap + |sput + |pop + |pop + |pop + |jump @_lbl_131 + |@_lbl_183: + |push x0000000000000000000000000000000000000000000000000000000000000000 + |dup + |call @stdlib_evm_sget + |swap + |pop + |swap + |pop + |jump @_lbl_151 + |push "Invalid" + |throw + |@stdlib_evm_sget: + |dup + |sexist + |jumpi @stdlib_evm_sget_non_zero + |pop + |push x0000000000000000000000000000000000000000000000000000000000000000 + |ret + |@stdlib_evm_sget_non_zero: + |sget + |ret + |@convert_result: + |dup + |push "get" + |eq + |not + |jumpi @convert_result_not_get + |pop + |push int8(4) + |cast + |stop + |@convert_result_not_get: + |dup + |push "set" + |eq + |not + |jumpi @convert_result_not_set + |pop + |stop + |@convert_result_not_set:""".stripMargin + } + + 'SimpleToken - { + val Right(ops) = Parser.parseWithIndices(readSolidityBinFile("SimpleToken/SimpleToken.bin")) + val Right(abi) = AbiParser.parseAbi(readSolidityABI("SimpleToken/SimpleToken.abi")) + val Right(asm) = Translator.translateActualContract(ops, abi) + + PravdaAssembler.render(asm) ==> + """@__start_evm_program: + |push int32(1024) + |push int8(1) + |new_array + |swap + |dup + |push "balances" + |eq + |not + |jumpi @not_balances + |push int32(3) + |swapn + |swap + |push int32(2) + |swapn + |push x + |swap + |push int8(14) + |cast + |push int32(8) + |scall + |concat + |push x00000000 + |concat + |swap + |push null + |jump @_lbl_103 + |@not_balances: + |dup + |push "balanceOf" + |eq + |not + |jumpi @not_balanceOf + |push int32(3) + |swapn + |swap + |push int32(2) + |swapn + |push x + |swap + |push int8(14) + |cast + |push int32(8) + |scall + |concat + |push x00000000 + |concat + |swap + |push null + |jump @_lbl_204 + |@not_balanceOf: + |dup + |push "transfer" + |eq + |not + |jumpi @not_transfer + |push int32(4) + |swapn + |push int32(3) + |swapn + |swap + |push int32(3) + |swapn + |push int32(2) + |swapn + |push x + |swap + |push int8(14) + |cast + |push int32(8) + |scall + |concat + |swap + |push int8(14) + |cast + |push int32(8) + |scall + |concat + |push x00000000 + |concat + |swap + |push null + |jump @_lbl_305 + |@not_transfer: + |dup + |push "emitTokens" + |eq + |not + |jumpi @not_emitTokens + |push int32(4) + |swapn + |push int32(3) + |swapn + |swap + |push int32(3) + |swapn + |push int32(2) + |swapn + |push x + |swap + |push int8(14) + |cast + |push int32(8) + |scall + |concat + |swap + |push int8(14) + |cast + |push int32(8) + |scall + |concat + |push x00000000 + |concat + |swap + |push null + |jump @_lbl_420 + |@not_emitTokens: + |@_lbl_98: + |push x0000000000000000000000000000000000000000000000000000000000000000 + |dup + |push "Revert" + |throw + |@_lbl_103: + |@_lbl_115: + |pop + |push x00B6000000000000000000000000000000000000000000000000000000000000 + |push x0400000000000000000000000000000000000000000000000000000000000000 + |dup + |push int32(5) + |dupn + |length + |push int8(4) + |cast + |swap + |push int8(4) + |cast + |swap + |swap + |push bigint(-1) + |mul + |add + |push int8(14) + |cast + |push int8(8) + |scall + |push x2000000000000000000000000000000000000000000000000000000000000000 + |push int32(2) + |dupn + |push int8(4) + |cast + |swap + |push int8(4) + |cast + |swap + |lt + |push int8(14) + |cast + |push int8(8) + |scall + |push x0000000000000000000000000000000000000000000000000000000000000000 + |eq + |push int8(14) + |cast + |push int8(8) + |scall + |push x008A000000000000000000000000000000000000000000000000000000000000 + |pop + |push int8(9) + |cast + |jumpi @_lbl_138 + |push x0000000000000000000000000000000000000000000000000000000000000000 + |dup + |push "Revert" + |throw + |@_lbl_138: + |push int32(2) + |dupn + |push int8(4) + |cast + |swap + |push int8(4) + |cast + |swap + |add + |push int8(14) + |cast + |push int8(8) + |scall + |swap + |dup + |dup + |push int8(4) + |cast + |push int32(7) + |dupn + |swap + |dup + |push int32(32) + |add + |swap + |slice + |push xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000000 + |and + |swap + |push x2000000000000000000000000000000000000000000000000000000000000000 + |push int8(4) + |cast + |swap + |push int8(4) + |cast + |swap + |add + |push int8(14) + |cast + |push int8(8) + |scall + |swap + |push int32(4) + |swapn + |push int32(3) + |swapn + |swap + |pop + |pop + |pop + |push x0217000000000000000000000000000000000000000000000000000000000000 + |pop + |jump @_lbl_535 + |@_lbl_182: + |push x4000000000000000000000000000000000000000000000000000000000000000 + |push bigint(32) + |swap + |push int8(4) + |cast + |push int32(5) + |dupn + |push int8(6) + |scall + |dup + |push int32(3) + |dupn + |push int32(2) + |dupn + |swap + |push int8(8) + |scall + |swap + |push int8(4) + |cast + |push int32(7) + |dupn + |push int8(7) + |scall + |push int32(6) + |swapn + |pop + |push x2000000000000000000000000000000000000000000000000000000000000000 + |push int8(4) + |cast + |swap + |push int8(4) + |cast + |swap + |add + |push int8(14) + |cast + |push int8(8) + |scall + |push int32(3) + |swapn + |pop + |pop + |push x4000000000000000000000000000000000000000000000000000000000000000 + |push bigint(32) + |swap + |push int8(4) + |cast + |push int32(5) + |dupn + |push int8(6) + |scall + |dup + |push int32(3) + |swapn + |push int8(4) + |cast + |swap + |push int8(4) + |cast + |swap + |swap + |push bigint(-1) + |mul + |add + |push int8(14) + |cast + |push int8(8) + |scall + |swap + |push int8(4) + |cast + |swap + |push int8(4) + |cast + |swap + |push int32(3) + |dupn + |push int8(6) + |scall + |swap + |pop + |swap + |pop + |swap + |jump @convert_result + |@_lbl_204: + |@_lbl_216: + |pop + |push x011B000000000000000000000000000000000000000000000000000000000000 + |push x0400000000000000000000000000000000000000000000000000000000000000 + |dup + |push int32(5) + |dupn + |length + |push int8(4) + |cast + |swap + |push int8(4) + |cast + |swap + |swap + |push bigint(-1) + |mul + |add + |push int8(14) + |cast + |push int8(8) + |scall + |push x2000000000000000000000000000000000000000000000000000000000000000 + |push int32(2) + |dupn + |push int8(4) + |cast + |swap + |push int8(4) + |cast + |swap + |lt + |push int8(14) + |cast + |push int8(8) + |scall + |push x0000000000000000000000000000000000000000000000000000000000000000 + |eq + |push int8(14) + |cast + |push int8(8) + |scall + |push x00EF000000000000000000000000000000000000000000000000000000000000 + |pop + |push int8(9) + |cast + |jumpi @_lbl_239 + |push x0000000000000000000000000000000000000000000000000000000000000000 + |dup + |push "Revert" + |throw + |@_lbl_239: + |push int32(2) + |dupn + |push int8(4) + |cast + |swap + |push int8(4) + |cast + |swap + |add + |push int8(14) + |cast + |push int8(8) + |scall + |swap + |dup + |dup + |push int8(4) + |cast + |push int32(7) + |dupn + |swap + |dup + |push int32(32) + |add + |swap + |slice + |push xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000000 + |and + |swap + |push x2000000000000000000000000000000000000000000000000000000000000000 + |push int8(4) + |cast + |swap + |push int8(4) + |cast + |swap + |add + |push int8(14) + |cast + |push int8(8) + |scall + |swap + |push int32(4) + |swapn + |push int32(3) + |swapn + |swap + |pop + |pop + |pop + |push x022F000000000000000000000000000000000000000000000000000000000000 + |pop + |jump @_lbl_559 + |@_lbl_283: + |push x4000000000000000000000000000000000000000000000000000000000000000 + |push bigint(32) + |swap + |push int8(4) + |cast + |push int32(4) + |dupn + |push int8(6) + |scall + |dup + |push int32(3) + |dupn + |push int32(2) + |dupn + |swap + |push int8(8) + |scall + |swap + |push int8(4) + |cast + |push int32(6) + |dupn + |push int8(7) + |scall + |push int32(5) + |swapn + |pop + |push x2000000000000000000000000000000000000000000000000000000000000000 + |push int8(4) + |cast + |swap + |push int8(4) + |cast + |swap + |add + |push int8(14) + |cast + |push int8(8) + |scall + |push int32(3) + |swapn + |pop + |pop + |push x4000000000000000000000000000000000000000000000000000000000000000 + |push bigint(32) + |swap + |push int8(4) + |cast + |push int32(4) + |dupn + |push int8(6) + |scall + |dup + |push int32(3) + |swapn + |push int8(4) + |cast + |swap + |push int8(4) + |cast + |swap + |swap + |push bigint(-1) + |mul + |add + |push int8(14) + |cast + |push int8(8) + |scall + |swap + |push int8(4) + |cast + |swap + |push int8(4) + |cast + |swap + |push int32(3) + |dupn + |push int8(6) + |scall + |swap + |pop + |swap + |pop + |swap + |jump @convert_result + |@_lbl_305: + |@_lbl_317: + |pop + |push x018A000000000000000000000000000000000000000000000000000000000000 + |push x0400000000000000000000000000000000000000000000000000000000000000 + |dup + |push int32(5) + |dupn + |length + |push int8(4) + |cast + |swap + |push int8(4) + |cast + |swap + |swap + |push bigint(-1) + |mul + |add + |push int8(14) + |cast + |push int8(8) + |scall + |push x4000000000000000000000000000000000000000000000000000000000000000 + |push int32(2) + |dupn + |push int8(4) + |cast + |swap + |push int8(4) + |cast + |swap + |lt + |push int8(14) + |cast + |push int8(8) + |scall + |push x0000000000000000000000000000000000000000000000000000000000000000 + |eq + |push int8(14) + |cast + |push int8(8) + |scall + |push x0154000000000000000000000000000000000000000000000000000000000000 + |pop + |push int8(9) + |cast + |jumpi @_lbl_340 + |push x0000000000000000000000000000000000000000000000000000000000000000 + |dup + |push "Revert" + |throw + |@_lbl_340: + |push int32(2) + |dupn + |push int8(4) + |cast + |swap + |push int8(4) + |cast + |swap + |add + |push int8(14) + |cast + |push int8(8) + |scall + |swap + |dup + |dup + |push int8(4) + |cast + |push int32(7) + |dupn + |swap + |dup + |push int32(32) + |add + |swap + |slice + |push xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000000 + |and + |swap + |push x2000000000000000000000000000000000000000000000000000000000000000 + |push int8(4) + |cast + |swap + |push int8(4) + |cast + |swap + |add + |push int8(14) + |cast + |push int8(8) + |scall + |swap + |push int32(4) + |swapn + |push int32(3) + |swapn + |swap + |dup + |push int8(4) + |cast + |push int32(8) + |dupn + |swap + |dup + |push int32(32) + |add + |swap + |slice + |swap + |push x2000000000000000000000000000000000000000000000000000000000000000 + |push int8(4) + |cast + |swap + |push int8(4) + |cast + |swap + |add + |push int8(14) + |cast + |push int8(8) + |scall + |swap + |push int32(4) + |swapn + |push int32(3) + |swapn + |swap + |pop + |pop + |pop + |push x0277000000000000000000000000000000000000000000000000000000000000 + |pop + |jump @_lbl_631 + |@_lbl_394: + |push x4000000000000000000000000000000000000000000000000000000000000000 + |push bigint(32) + |swap + |push int8(4) + |cast + |push int32(4) + |dupn + |push int8(6) + |scall + |dup + |push int32(3) + |dupn + |push x0000000000000000000000000000000000000000000000000000000000000000 + |eq + |push int8(14) + |cast + |push int8(8) + |scall + |push x0000000000000000000000000000000000000000000000000000000000000000 + |eq + |push int8(14) + |cast + |push int8(8) + |scall + |push x0000000000000000000000000000000000000000000000000000000000000000 + |eq + |push int8(14) + |cast + |push int8(8) + |scall + |push x0000000000000000000000000000000000000000000000000000000000000000 + |eq + |push int8(14) + |cast + |push int8(8) + |scall + |push int32(2) + |dupn + |swap + |push int8(8) + |scall + |swap + |push int8(4) + |cast + |push int32(6) + |dupn + |push int8(7) + |scall + |push int32(5) + |swapn + |pop + |push x2000000000000000000000000000000000000000000000000000000000000000 + |push int8(4) + |cast + |swap + |push int8(4) + |cast + |swap + |add + |push int8(14) + |cast + |push int8(8) + |scall + |push int32(3) + |swapn + |pop + |pop + |push x4000000000000000000000000000000000000000000000000000000000000000 + |push bigint(32) + |swap + |push int8(4) + |cast + |push int32(4) + |dupn + |push int8(6) + |scall + |dup + |push int32(3) + |swapn + |push int8(4) + |cast + |swap + |push int8(4) + |cast + |swap + |swap + |push bigint(-1) + |mul + |add + |push int8(14) + |cast + |push int8(8) + |scall + |swap + |push int8(4) + |cast + |swap + |push int8(4) + |cast + |swap + |push int32(3) + |dupn + |push int8(6) + |scall + |swap + |pop + |swap + |pop + |swap + |jump @convert_result + |@_lbl_420: + |@_lbl_432: + |pop + |push x01FD000000000000000000000000000000000000000000000000000000000000 + |push x0400000000000000000000000000000000000000000000000000000000000000 + |dup + |push int32(5) + |dupn + |length + |push int8(4) + |cast + |swap + |push int8(4) + |cast + |swap + |swap + |push bigint(-1) + |mul + |add + |push int8(14) + |cast + |push int8(8) + |scall + |push x4000000000000000000000000000000000000000000000000000000000000000 + |push int32(2) + |dupn + |push int8(4) + |cast + |swap + |push int8(4) + |cast + |swap + |lt + |push int8(14) + |cast + |push int8(8) + |scall + |push x0000000000000000000000000000000000000000000000000000000000000000 + |eq + |push int8(14) + |cast + |push int8(8) + |scall + |push x01C7000000000000000000000000000000000000000000000000000000000000 + |pop + |push int8(9) + |cast + |jumpi @_lbl_455 + |push x0000000000000000000000000000000000000000000000000000000000000000 + |dup + |push "Revert" + |throw + |@_lbl_455: + |push int32(2) + |dupn + |push int8(4) + |cast + |swap + |push int8(4) + |cast + |swap + |add + |push int8(14) + |cast + |push int8(8) + |scall + |swap + |dup + |dup + |push int8(4) + |cast + |push int32(7) + |dupn + |swap + |dup + |push int32(32) + |add + |swap + |slice + |push xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000000 + |and + |swap + |push x2000000000000000000000000000000000000000000000000000000000000000 + |push int8(4) + |cast + |swap + |push int8(4) + |cast + |swap + |add + |push int8(14) + |cast + |push int8(8) + |scall + |swap + |push int32(4) + |swapn + |push int32(3) + |swapn + |swap + |dup + |push int8(4) + |cast + |push int32(8) + |dupn + |swap + |dup + |push int32(32) + |add + |swap + |slice + |swap + |push x2000000000000000000000000000000000000000000000000000000000000000 + |push int8(4) + |cast + |swap + |push int8(4) + |cast + |swap + |add + |push int8(14) + |cast + |push int8(8) + |scall + |swap + |push int32(4) + |swapn + |push int32(3) + |swapn + |swap + |pop + |pop + |pop + |push x0389000000000000000000000000000000000000000000000000000000000000 + |pop + |jump @_lbl_905 + |@_lbl_509: + |push x4000000000000000000000000000000000000000000000000000000000000000 + |push bigint(32) + |swap + |push int8(4) + |cast + |push int32(4) + |dupn + |push int8(6) + |scall + |dup + |push int32(3) + |dupn + |push x0000000000000000000000000000000000000000000000000000000000000000 + |eq + |push int8(14) + |cast + |push int8(8) + |scall + |push x0000000000000000000000000000000000000000000000000000000000000000 + |eq + |push int8(14) + |cast + |push int8(8) + |scall + |push x0000000000000000000000000000000000000000000000000000000000000000 + |eq + |push int8(14) + |cast + |push int8(8) + |scall + |push x0000000000000000000000000000000000000000000000000000000000000000 + |eq + |push int8(14) + |cast + |push int8(8) + |scall + |push int32(2) + |dupn + |swap + |push int8(8) + |scall + |swap + |push int8(4) + |cast + |push int32(6) + |dupn + |push int8(7) + |scall + |push int32(5) + |swapn + |pop + |push x2000000000000000000000000000000000000000000000000000000000000000 + |push int8(4) + |cast + |swap + |push int8(4) + |cast + |swap + |add + |push int8(14) + |cast + |push int8(8) + |scall + |push int32(3) + |swapn + |pop + |pop + |push x4000000000000000000000000000000000000000000000000000000000000000 + |push bigint(32) + |swap + |push int8(4) + |cast + |push int32(4) + |dupn + |push int8(6) + |scall + |dup + |push int32(3) + |swapn + |push int8(4) + |cast + |swap + |push int8(4) + |cast + |swap + |swap + |push bigint(-1) + |mul + |add + |push int8(14) + |cast + |push int8(8) + |scall + |swap + |push int8(4) + |cast + |swap + |push int8(4) + |cast + |swap + |push int32(3) + |dupn + |push int8(6) + |scall + |swap + |pop + |swap + |pop + |swap + |jump @convert_result + |@_lbl_535: + |push x0000000000000000000000000000000000000000000000000000000000000000 + |push x2000000000000000000000000000000000000000000000000000000000000000 + |swap + |push int8(8) + |scall + |swap + |push int8(4) + |cast + |push int32(5) + |dupn + |push int8(7) + |scall + |push int32(4) + |swapn + |pop + |dup + |push x0000000000000000000000000000000000000000000000000000000000000000 + |swap + |push int8(8) + |scall + |swap + |push int8(4) + |cast + |push int32(5) + |dupn + |push int8(7) + |scall + |push int32(4) + |swapn + |pop + |push x4000000000000000000000000000000000000000000000000000000000000000 + |push x0000000000000000000000000000000000000000000000000000000000000000 + |push int8(4) + |cast + |swap + |push int8(4) + |cast + |swap + |push int32(5) + |dupn + |push int8(6) + |scall + |push int8(9) + |scall + |push x0000000000000000000000000000000000000000000000000000000000000000 + |push int32(3) + |swapn + |pop + |swap + |pop + |call @stdlib_evm_sget + |push int32(2) + |dupn + |pop + |jump @_lbl_182 + |@_lbl_559: + |push x0000000000000000000000000000000000000000000000000000000000000000 + |dup + |push x0000000000000000000000000000000000000000000000000000000000000000 + |push int32(4) + |dupn + |push xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000000 + |and + |push xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000000 + |and + |push int32(2) + |dupn + |swap + |push int8(8) + |scall + |swap + |push int8(4) + |cast + |push int32(8) + |dupn + |push int8(7) + |scall + |push int32(7) + |swapn + |pop + |push x2000000000000000000000000000000000000000000000000000000000000000 + |push int8(4) + |cast + |swap + |push int8(4) + |cast + |swap + |add + |push int8(14) + |cast + |push int8(8) + |scall + |swap + |push int32(2) + |dupn + |swap + |push int8(8) + |scall + |swap + |push int8(4) + |cast + |push int32(7) + |dupn + |push int8(7) + |scall + |push int32(6) + |swapn + |pop + |push x2000000000000000000000000000000000000000000000000000000000000000 + |push int8(4) + |cast + |swap + |push int8(4) + |cast + |swap + |add + |push int8(14) + |cast + |push int8(8) + |scall + |push x0000000000000000000000000000000000000000000000000000000000000000 + |push int8(4) + |cast + |swap + |push int8(4) + |cast + |swap + |push int32(6) + |dupn + |push int8(6) + |scall + |push int8(9) + |scall + |call @stdlib_evm_sget + |swap + |pop + |push int32(3) + |swapn + |swap + |pop + |pop + |jump @_lbl_283 + |@_lbl_631: + |push x0000000000000000000000000000000000000000000000000000000000000000 + |push int32(2) + |dupn + |push x0000000000000000000000000000000000000000000000000000000000000000 + |dup + |from + |push xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000000 + |and + |push xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000000 + |and + |push int32(2) + |dupn + |swap + |push int8(8) + |scall + |swap + |push int8(4) + |cast + |push int32(10) + |dupn + |push int8(7) + |scall + |push int32(9) + |swapn + |pop + |push x2000000000000000000000000000000000000000000000000000000000000000 + |push int8(4) + |cast + |swap + |push int8(4) + |cast + |swap + |add + |push int8(14) + |cast + |push int8(8) + |scall + |swap + |push int32(2) + |dupn + |swap + |push int8(8) + |scall + |swap + |push int8(4) + |cast + |push int32(9) + |dupn + |push int8(7) + |scall + |push int32(8) + |swapn + |pop + |push x2000000000000000000000000000000000000000000000000000000000000000 + |push int8(4) + |cast + |swap + |push int8(4) + |cast + |swap + |add + |push int8(14) + |cast + |push int8(8) + |scall + |push x0000000000000000000000000000000000000000000000000000000000000000 + |push int8(4) + |cast + |swap + |push int8(4) + |cast + |swap + |push int32(8) + |dupn + |push int8(6) + |scall + |push int8(9) + |scall + |call @stdlib_evm_sget + |push int8(4) + |cast + |swap + |push int8(4) + |cast + |swap + |swap + |push bigint(-1) + |mul + |add + |push int8(14) + |cast + |push int8(8) + |scall + |push x0000000000000000000000000000000000000000000000000000000000000000 + |dup + |from + |push xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000000 + |and + |push xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000000 + |and + |push int32(2) + |dupn + |swap + |push int8(8) + |scall + |swap + |push int8(4) + |cast + |push int32(10) + |dupn + |push int8(7) + |scall + |push int32(9) + |swapn + |pop + |push x2000000000000000000000000000000000000000000000000000000000000000 + |push int8(4) + |cast + |swap + |push int8(4) + |cast + |swap + |add + |push int8(14) + |cast + |push int8(8) + |scall + |swap + |push int32(2) + |dupn + |swap + |push int8(8) + |scall + |swap + |push int8(4) + |cast + |push int32(9) + |dupn + |push int8(7) + |scall + |push int32(8) + |swapn + |pop + |push x2000000000000000000000000000000000000000000000000000000000000000 + |push int8(4) + |cast + |swap + |push int8(4) + |cast + |swap + |add + |push int8(14) + |cast + |push int8(8) + |scall + |push x0000000000000000000000000000000000000000000000000000000000000000 + |push int8(4) + |cast + |swap + |push int8(4) + |cast + |swap + |push int32(8) + |dupn + |push int8(6) + |scall + |push int8(9) + |scall + |push int32(2) + |dupn + |swap + |sput + |pop + |push int32(2) + |dupn + |push x0000000000000000000000000000000000000000000000000000000000000000 + |dup + |push int32(6) + |dupn + |push xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000000 + |and + |push xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000000 + |and + |push int32(2) + |dupn + |swap + |push int8(8) + |scall + |swap + |push int8(4) + |cast + |push int32(10) + |dupn + |push int8(7) + |scall + |push int32(9) + |swapn + |pop + |push x2000000000000000000000000000000000000000000000000000000000000000 + |push int8(4) + |cast + |swap + |push int8(4) + |cast + |swap + |add + |push int8(14) + |cast + |push int8(8) + |scall + |swap + |push int32(2) + |dupn + |swap + |push int8(8) + |scall + |swap + |push int8(4) + |cast + |push int32(9) + |dupn + |push int8(7) + |scall + |push int32(8) + |swapn + |pop + |push x2000000000000000000000000000000000000000000000000000000000000000 + |push int8(4) + |cast + |swap + |push int8(4) + |cast + |swap + |add + |push int8(14) + |cast + |push int8(8) + |scall + |push x0000000000000000000000000000000000000000000000000000000000000000 + |push int8(4) + |cast + |swap + |push int8(4) + |cast + |swap + |push int32(8) + |dupn + |push int8(6) + |scall + |push int8(9) + |scall + |call @stdlib_evm_sget + |push int8(4) + |cast + |swap + |push int8(4) + |cast + |swap + |add + |push int8(14) + |cast + |push int8(8) + |scall + |push x0000000000000000000000000000000000000000000000000000000000000000 + |dup + |push int32(6) + |dupn + |push xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000000 + |and + |push xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000000 + |and + |push int32(2) + |dupn + |swap + |push int8(8) + |scall + |swap + |push int8(4) + |cast + |push int32(10) + |dupn + |push int8(7) + |scall + |push int32(9) + |swapn + |pop + |push x2000000000000000000000000000000000000000000000000000000000000000 + |push int8(4) + |cast + |swap + |push int8(4) + |cast + |swap + |add + |push int8(14) + |cast + |push int8(8) + |scall + |swap + |push int32(2) + |dupn + |swap + |push int8(8) + |scall + |swap + |push int8(4) + |cast + |push int32(9) + |dupn + |push int8(7) + |scall + |push int32(8) + |swapn + |pop + |push x2000000000000000000000000000000000000000000000000000000000000000 + |push int8(4) + |cast + |swap + |push int8(4) + |cast + |swap + |add + |push int8(14) + |cast + |push int8(8) + |scall + |push x0000000000000000000000000000000000000000000000000000000000000000 + |push int8(4) + |cast + |swap + |push int8(4) + |cast + |swap + |push int32(8) + |dupn + |push int8(6) + |scall + |push int8(9) + |scall + |push int32(2) + |dupn + |swap + |sput + |pop + |push x0100000000000000000000000000000000000000000000000000000000000000 + |swap + |pop + |push int32(4) + |swapn + |push int32(3) + |swapn + |pop + |pop + |pop + |jump @_lbl_394 + |@_lbl_905: + |push x0000000000000000000000000000000000000000000000000000000000000000 + |push int32(2) + |dupn + |push x0000000000000000000000000000000000000000000000000000000000000000 + |dup + |push int32(6) + |dupn + |push xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000000 + |and + |push xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000000 + |and + |push int32(2) + |dupn + |swap + |push int8(8) + |scall + |swap + |push int8(4) + |cast + |push int32(10) + |dupn + |push int8(7) + |scall + |push int32(9) + |swapn + |pop + |push x2000000000000000000000000000000000000000000000000000000000000000 + |push int8(4) + |cast + |swap + |push int8(4) + |cast + |swap + |add + |push int8(14) + |cast + |push int8(8) + |scall + |swap + |push int32(2) + |dupn + |swap + |push int8(8) + |scall + |swap + |push int8(4) + |cast + |push int32(9) + |dupn + |push int8(7) + |scall + |push int32(8) + |swapn + |pop + |push x2000000000000000000000000000000000000000000000000000000000000000 + |push int8(4) + |cast + |swap + |push int8(4) + |cast + |swap + |add + |push int8(14) + |cast + |push int8(8) + |scall + |push x0000000000000000000000000000000000000000000000000000000000000000 + |push int8(4) + |cast + |swap + |push int8(4) + |cast + |swap + |push int32(8) + |dupn + |push int8(6) + |scall + |push int8(9) + |scall + |call @stdlib_evm_sget + |push int8(4) + |cast + |swap + |push int8(4) + |cast + |swap + |add + |push int8(14) + |cast + |push int8(8) + |scall + |push x0000000000000000000000000000000000000000000000000000000000000000 + |dup + |push int32(6) + |dupn + |push xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000000 + |and + |push xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000000 + |and + |push int32(2) + |dupn + |swap + |push int8(8) + |scall + |swap + |push int8(4) + |cast + |push int32(10) + |dupn + |push int8(7) + |scall + |push int32(9) + |swapn + |pop + |push x2000000000000000000000000000000000000000000000000000000000000000 + |push int8(4) + |cast + |swap + |push int8(4) + |cast + |swap + |add + |push int8(14) + |cast + |push int8(8) + |scall + |swap + |push int32(2) + |dupn + |swap + |push int8(8) + |scall + |swap + |push int8(4) + |cast + |push int32(9) + |dupn + |push int8(7) + |scall + |push int32(8) + |swapn + |pop + |push x2000000000000000000000000000000000000000000000000000000000000000 + |push int8(4) + |cast + |swap + |push int8(4) + |cast + |swap + |add + |push int8(14) + |cast + |push int8(8) + |scall + |push x0000000000000000000000000000000000000000000000000000000000000000 + |push int8(4) + |cast + |swap + |push int8(4) + |cast + |swap + |push int32(8) + |dupn + |push int8(6) + |scall + |push int8(9) + |scall + |push int32(2) + |dupn + |swap + |sput + |pop + |push x0100000000000000000000000000000000000000000000000000000000000000 + |swap + |pop + |push int32(4) + |swapn + |push int32(3) + |swapn + |pop + |pop + |pop + |jump @_lbl_509 + |push "Invalid" + |throw + |@stdlib_evm_sget: + |dup + |sexist + |jumpi @stdlib_evm_sget_non_zero + |pop + |push x0000000000000000000000000000000000000000000000000000000000000000 + |ret + |@stdlib_evm_sget_non_zero: + |sget + |ret + |@convert_result: + |dup + |push "emitTokens" + |eq + |not + |jumpi @convert_result_not_emitTokens + |pop + |push int8(9) + |cast + |stop + |@convert_result_not_emitTokens: + |dup + |push "transfer" + |eq + |not + |jumpi @convert_result_not_transfer + |pop + |push int8(9) + |cast + |stop + |@convert_result_not_transfer: + |dup + |push "balanceOf" + |eq + |not + |jumpi @convert_result_not_balanceOf + |pop + |push int8(4) + |cast + |stop + |@convert_result_not_balanceOf: + |dup + |push "balances" + |eq + |not + |jumpi @convert_result_not_balances + |pop + |push int8(4) + |cast + |stop + |@convert_result_not_balances:""".stripMargin + } } } - } diff --git a/faucet/src/main/resources/application.conf b/faucet/src/main/resources/application.conf new file mode 100644 index 00000000..ee3b4519 --- /dev/null +++ b/faucet/src/main/resources/application.conf @@ -0,0 +1,9 @@ +faucet { + host = "0.0.0.0" + host = ${?FAUCET_HTTP_HOST} + port = 9000 + port = ${?FAUCET_HTTP_PORT} + testnet-endpoint = ${?TESTNET_ENDPOINT} + wallet-address = ${?WALLET_ADDRESS} + wallet-private-key = ${?WALLET_PRIVATE_KEY} +} diff --git a/faucet/src/main/scala/pravda/faucet/Config.scala b/faucet/src/main/scala/pravda/faucet/Config.scala new file mode 100644 index 00000000..56d8e635 --- /dev/null +++ b/faucet/src/main/scala/pravda/faucet/Config.scala @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2018 Expload.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package pravda.faucet + +final case class FaucetConfig(host: String, + port: Int, + testnetEndpoint: String, + walletAddress: String, + walletPrivateKey: String) + +object Config { + val faucetConfig = pureconfig.loadConfigOrThrow[FaucetConfig]("faucet") +} diff --git a/faucet/src/main/scala/pravda/faucet/Faucet.scala b/faucet/src/main/scala/pravda/faucet/Faucet.scala new file mode 100644 index 00000000..68495875 --- /dev/null +++ b/faucet/src/main/scala/pravda/faucet/Faucet.scala @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2018 Expload.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package pravda.faucet + +import akka.actor.ActorSystem +import akka.http.scaladsl.Http +import akka.http.scaladsl.server.Directives._ +import akka.stream.ActorMaterializer + +import scala.concurrent.duration._ +import scala.concurrent.{Await, ExecutionContextExecutor} +import scala.util.Success + +object Faucet extends App { + + private implicit val system: ActorSystem = ActorSystem("pravda-system") + private implicit val materializer: ActorMaterializer = ActorMaterializer() + private implicit val executionContext: ExecutionContextExecutor = system.dispatcher + + val config = Config.faucetConfig + + val httpServer = { + val healthz = pathPrefix("healthz")(complete("faucet-service: OK")) + val gui = pathPrefix("ui")(new GuiRoute().route) + + Http().bindAndHandle(healthz ~ gui, config.host, config.port) andThen { + case Success(_) => println(s"API server started at ${config.host}:${config.port}") + } + } + + sys.addShutdownHook { + val tcp = Await.result(httpServer, 10.seconds) + print("Shutting down API server...") + Await.result(tcp.unbind(), 10.seconds) + system.terminate() + println(s"${Console.GREEN} done${Console.RESET}") + } + +} diff --git a/faucet/src/main/scala/pravda/faucet/GuiRoute.scala b/faucet/src/main/scala/pravda/faucet/GuiRoute.scala new file mode 100644 index 00000000..0a110f44 --- /dev/null +++ b/faucet/src/main/scala/pravda/faucet/GuiRoute.scala @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2018 Expload.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package pravda.faucet + +import akka.actor.ActorSystem +import akka.http.scaladsl.server.Route +import akka.stream.ActorMaterializer +import korolev.Context +import korolev.akkahttp._ +import korolev.execution._ +import korolev.server.{KorolevServiceConfig, ServerRouter} +import korolev.state.StateStorage +import korolev.state.javaSerialization._ +import pravda.common.bytes +import pravda.common.domain.NativeCoin +import pravda.node.client.impl.{CompilersLanguageImpl, NodeLanguageImpl} + +import scala.concurrent.Future + +class GuiRoute(implicit system: ActorSystem, materializer: ActorMaterializer) { + + import GuiRoute._ + import globalContext._ + import symbolDsl._ + + private val addressField = elementId() + private val xcoinsField = elementId() + + private val node = new NodeLanguageImpl() + private val compilers = new CompilersLanguageImpl() + + private val service = akkaHttpService( + KorolevServiceConfig[Future, GuiState, Any]( + serverRouter = ServerRouter + .empty[Future, GuiState] + .withRootPath("/ui/"), + stateStorage = StateStorage.default(AccrueCoins(None)), + head = Seq( + 'link ('href /= "https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.1/css/bulma.min.css", 'rel /= "stylesheet"), + 'link ( + 'href /= "https://use.fontawesome.com/releases/v5.0.13/css/all.css", + 'rel /= "stylesheet", + 'crossorigin /= "anonymous", + 'integrity /= "sha384-DNOHZ68U8hZfKXOrtjWvjxusGo9WQnrNx2sqG0tfsghAvtVlRW3tvkXWZh58N9jp" + ) + ), + render = { + case AccrueCoins(msg) => + 'body ( + 'div ( + 'class /= "container", + 'form ( + 'display @= "flex", + 'flexDirection @= "column", + 'input ('class /= "input", 'margin @= 10, addressField, 'placeholder /= "Address", 'value /= ""), + 'input ('class /= "input", + 'margin @= 10, + xcoinsField, + 'placeholder /= "Number of Native Coins", + 'value /= "1000000"), + 'div ( + 'button ( + 'class /= "button is-link", + 'margin @= 10, + 'width @= 200, + "Accrue", + event('click) { + access => + for { + address <- access.valueOf(addressField) + coins <- access.valueOf(xcoinsField) + codeOrError <- compilers.asm(s"push x$address push bigint($coins) transfer") + resOrError <- codeOrError match { + case Left(err) => Future.successful(Left(s"Error during request construction: $err")) + case Right(code) => + node + .singAndBroadcastTransaction( + Config.faucetConfig.testnetEndpoint, + bytes.hex2byteString(Config.faucetConfig.walletAddress), + bytes.hex2byteString(Config.faucetConfig.walletPrivateKey), + None, + 10000L, + NativeCoin @@ 1L, + None, + code + ) + .map(_.left.map(e => s"Error during http request: $e")) + } + _ <- resOrError match { + case Left(err) => access.transition(_ => ErrorScreen(err)) + case Right(res) => + res.executionResult match { + case Left(runtimeError) => + access.transition(_ => + ErrorScreen(s"Runtime error during transferring: ${runtimeError.error}")) + case Right(finalState) => + access.transition(_ => + AccrueCoins(Some(s"Successfully transferred $coins to $address"))) + } + } + } yield {} + } + ) + ) + ), + msg match { + case Some(m) => 'div ('class /= "notification is-success", m) + case None => void + } + ) + ) + case ErrorScreen(error) => + 'body ( + 'div ('class /= "container", 'div ('class /= "notification is-danger", error)) + ) + } + ) + ) + + val route: Route = service(AkkaHttpServerConfig()) +} + +object GuiRoute { + + sealed trait GuiState + + final case class AccrueCoins(successMessage: Option[String]) extends GuiState + final case class ErrorScreen(error: String) extends GuiState + + val globalContext: Context[Future, GuiState, Any] = + Context[Future, GuiState, Any] +} diff --git a/cli/src/main/scala/pravda/cli/languages/CodeGeneratorsLanguage.scala b/node-client/src/main/scala/pravda/node/client/CodeGeneratorsLanguage.scala similarity index 87% rename from cli/src/main/scala/pravda/cli/languages/CodeGeneratorsLanguage.scala rename to node-client/src/main/scala/pravda/node/client/CodeGeneratorsLanguage.scala index 0491d51b..f6da9b51 100644 --- a/cli/src/main/scala/pravda/cli/languages/CodeGeneratorsLanguage.scala +++ b/node-client/src/main/scala/pravda/node/client/CodeGeneratorsLanguage.scala @@ -15,12 +15,12 @@ * along with this program. If not, see . */ -package pravda.cli.languages +package pravda.node.client import com.google.protobuf.ByteString import scala.language.higherKinds trait CodeGeneratorsLanguage[F[_]] { - def dotnet(input: ByteString, excludeBigInteger: Boolean): F[List[(String, String)]] + def dotnet(input: ByteString): F[List[(String, String)]] } diff --git a/cli/src/main/scala/pravda/cli/languages/CompilersLanguage.scala b/node-client/src/main/scala/pravda/node/client/CompilersLanguage.scala similarity index 92% rename from cli/src/main/scala/pravda/cli/languages/CompilersLanguage.scala rename to node-client/src/main/scala/pravda/node/client/CompilersLanguage.scala index bc90d662..5d2d93c2 100644 --- a/cli/src/main/scala/pravda/cli/languages/CompilersLanguage.scala +++ b/node-client/src/main/scala/pravda/node/client/CompilersLanguage.scala @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package pravda.cli.languages +package pravda.node.client import com.google.protobuf.ByteString import pravda.vm.asm.Operation @@ -28,4 +28,5 @@ trait CompilersLanguage[F[_]] { def disasm(source: ByteString): F[String] def disasmToOps(source: ByteString): F[Seq[(Int, Operation)]] def dotnet(sources: Seq[(ByteString, Option[ByteString])], mainClass: Option[String]): F[Either[String, ByteString]] + def evm(source: ByteString, abi: ByteString): F[Either[String, ByteString]] } diff --git a/cli/src/main/scala/pravda/cli/languages/IoLanguage.scala b/node-client/src/main/scala/pravda/node/client/IoLanguage.scala similarity index 98% rename from cli/src/main/scala/pravda/cli/languages/IoLanguage.scala rename to node-client/src/main/scala/pravda/node/client/IoLanguage.scala index dd1bffdf..933e0345 100644 --- a/cli/src/main/scala/pravda/cli/languages/IoLanguage.scala +++ b/node-client/src/main/scala/pravda/node/client/IoLanguage.scala @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package pravda.cli.languages +package pravda.node.client import com.google.protobuf.ByteString diff --git a/cli/src/main/scala/pravda/cli/languages/NodeLanguage.scala b/node-client/src/main/scala/pravda/node/client/NodeLanguage.scala similarity index 91% rename from cli/src/main/scala/pravda/cli/languages/NodeLanguage.scala rename to node-client/src/main/scala/pravda/node/client/NodeLanguage.scala index f13626f2..fcb100b9 100644 --- a/cli/src/main/scala/pravda/cli/languages/NodeLanguage.scala +++ b/node-client/src/main/scala/pravda/node/client/NodeLanguage.scala @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package pravda.cli.languages +package pravda.node.client import com.google.protobuf.ByteString import pravda.common.domain.{Address, NativeCoin} @@ -35,4 +35,6 @@ trait NodeLanguage[F[_]] { wattPrice: NativeCoin, wattPayer: Option[Address], data: ByteString): F[Either[String, TransactionResult]] + + def execute(data: ByteString, address: Address, endpoint: String): F[Either[String, TransactionResult]] } diff --git a/cli/src/main/scala/pravda/cli/languages/RandomLanguage.scala b/node-client/src/main/scala/pravda/node/client/RandomLanguage.scala similarity index 96% rename from cli/src/main/scala/pravda/cli/languages/RandomLanguage.scala rename to node-client/src/main/scala/pravda/node/client/RandomLanguage.scala index f12b8da3..d4d13c97 100644 --- a/cli/src/main/scala/pravda/cli/languages/RandomLanguage.scala +++ b/node-client/src/main/scala/pravda/node/client/RandomLanguage.scala @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package pravda.cli.languages +package pravda.node.client import com.google.protobuf.ByteString diff --git a/cli/src/main/scala/pravda/cli/languages/VmLanguage.scala b/node-client/src/main/scala/pravda/node/client/VmLanguage.scala similarity index 97% rename from cli/src/main/scala/pravda/cli/languages/VmLanguage.scala rename to node-client/src/main/scala/pravda/node/client/VmLanguage.scala index 6b799f5d..5b672c9a 100644 --- a/cli/src/main/scala/pravda/cli/languages/VmLanguage.scala +++ b/node-client/src/main/scala/pravda/node/client/VmLanguage.scala @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package pravda.cli.languages +package pravda.node.client import com.google.protobuf.ByteString import pravda.vm.ExecutionResult diff --git a/cli/src/main/scala/pravda/cli/languages/impl/CodeGeneratorsLanguageImpl.scala b/node-client/src/main/scala/pravda/node/client/impl/CodeGeneratorsLanguageImpl.scala similarity index 78% rename from cli/src/main/scala/pravda/cli/languages/impl/CodeGeneratorsLanguageImpl.scala rename to node-client/src/main/scala/pravda/node/client/impl/CodeGeneratorsLanguageImpl.scala index f1fe7e90..d70d3217 100644 --- a/cli/src/main/scala/pravda/cli/languages/impl/CodeGeneratorsLanguageImpl.scala +++ b/node-client/src/main/scala/pravda/node/client/impl/CodeGeneratorsLanguageImpl.scala @@ -15,18 +15,17 @@ * along with this program. If not, see . */ -package pravda.cli.languages.impl +package pravda.node.client.impl import com.google.protobuf.ByteString -import pravda.cli.languages.CodeGeneratorsLanguage import pravda.codegen.dotnet.DotnetCodegen +import pravda.node.client.CodeGeneratorsLanguage import scala.concurrent.{ExecutionContext, Future} final class CodeGeneratorsLanguageImpl(implicit executionContext: ExecutionContext) extends CodeGeneratorsLanguage[Future] { - override def dotnet(input: ByteString, excludeBigInteger: Boolean): Future[List[(String, String)]] = Future { - val methods = DotnetCodegen.generate(input) - List(methods) + override def dotnet(input: ByteString): Future[List[(String, String)]] = Future { + DotnetCodegen.generate(input).toList } } diff --git a/cli/src/main/scala/pravda/cli/languages/impl/CompilersLanguageImpl.scala b/node-client/src/main/scala/pravda/node/client/impl/CompilersLanguageImpl.scala similarity index 77% rename from cli/src/main/scala/pravda/cli/languages/impl/CompilersLanguageImpl.scala rename to node-client/src/main/scala/pravda/node/client/impl/CompilersLanguageImpl.scala index cd89ca8c..c6414358 100644 --- a/cli/src/main/scala/pravda/cli/languages/impl/CompilersLanguageImpl.scala +++ b/node-client/src/main/scala/pravda/node/client/impl/CompilersLanguageImpl.scala @@ -15,15 +15,14 @@ * along with this program. If not, see . */ -package pravda.cli.languages - -package impl +package pravda.node.client.impl import com.google.protobuf.ByteString import pravda.dotnet.parser.FileParser import pravda.dotnet.translation.{Translator => DotnetTranslator} import pravda.vm.asm.{Operation, PravdaAssembler} import cats.implicits._ +import pravda.node.client.CompilersLanguage import scala.concurrent.{ExecutionContext, Future} @@ -57,4 +56,19 @@ final class CompilersLanguageImpl(implicit executionContext: ExecutionContext) e ops <- DotnetTranslator.translateAsm(files, mainClass).left.map(_.mkString) } yield PravdaAssembler.assemble(ops, saveLabels = true) } + + def evm(sourceBytes: ByteString, abiBytes: ByteString): Future[Either[String, ByteString]] = Future { + import pravda.evm.abi.parse.AbiParser._ + import pravda.evm.parse.Parser._ + import pravda.evm.translate.Translator._ + + val source = sourceBytes.toStringUtf8.sliding(2, 2).toArray.map(Integer.parseInt(_, 16).toByte).dropRight(43) + val abiS = abiBytes.toStringUtf8 + + for { + abi <- parseAbi(abiS) + ops <- parseWithIndices(source) + asmOps <- translateActualContract(ops, abi) + } yield PravdaAssembler.assemble(asmOps, saveLabels = true) + } } diff --git a/cli/src/main/scala/pravda/cli/languages/impl/IoLanguageImpl.scala b/node-client/src/main/scala/pravda/node/client/impl/IoLanguageImpl.scala similarity index 98% rename from cli/src/main/scala/pravda/cli/languages/impl/IoLanguageImpl.scala rename to node-client/src/main/scala/pravda/node/client/impl/IoLanguageImpl.scala index b2349f32..61645800 100644 --- a/cli/src/main/scala/pravda/cli/languages/impl/IoLanguageImpl.scala +++ b/node-client/src/main/scala/pravda/node/client/impl/IoLanguageImpl.scala @@ -15,9 +15,7 @@ * along with this program. If not, see . */ -package pravda.cli.languages - -package impl +package pravda.node.client.impl import java.io.File import java.nio.file.{Files, Paths} @@ -26,6 +24,7 @@ import java.nio.channels.Channels import java.nio.charset.StandardCharsets import com.google.protobuf.ByteString +import pravda.node.client.IoLanguage import scala.concurrent.{ExecutionContext, Future} import scala.sys.process.stdin diff --git a/cli/src/main/scala/pravda/cli/languages/impl/NodeLanguageImpl.scala b/node-client/src/main/scala/pravda/node/client/impl/NodeLanguageImpl.scala similarity index 77% rename from cli/src/main/scala/pravda/cli/languages/impl/NodeLanguageImpl.scala rename to node-client/src/main/scala/pravda/node/client/impl/NodeLanguageImpl.scala index 875d823b..564392a6 100644 --- a/cli/src/main/scala/pravda/cli/languages/impl/NodeLanguageImpl.scala +++ b/node-client/src/main/scala/pravda/node/client/impl/NodeLanguageImpl.scala @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package pravda.cli.languages.impl +package pravda.node.client.impl import akka.actor.ActorSystem import akka.http.scaladsl.Http @@ -23,9 +23,9 @@ import akka.http.scaladsl.model.{HttpEntity, HttpMethods, HttpRequest} import akka.stream.ActorMaterializer import akka.util.{ByteString => AkkaByteString} import com.google.protobuf.ByteString -import pravda.cli.languages.NodeLanguage import pravda.common.bytes import pravda.common.domain.{Address, NativeCoin} +import pravda.node.client.NodeLanguage import pravda.node.clients.AbciClient.RpcError import pravda.node.data.blockchain.Transaction.UnsignedTransaction import pravda.node.data.blockchain.TransactionData @@ -72,10 +72,11 @@ final class NodeLanguageImpl(implicit system: ActorSystem, ) val stx = { - val one = cryptography.signTransaction(PrivateKey @@ privateKey, tx) wattPayerPrivateKey match { - case Some(pk) => cryptography.addWattPayerSignature(PrivateKey @@ pk, one.copy(wattPayer = wattPayer)) - case None => one + case Some(pk) => + val one = cryptography.signTransaction(PrivateKey @@ privateKey, tx.copy(wattPayer = wattPayer)) + cryptography.addWattPayerSignature(PrivateKey @@ pk, one) + case None => cryptography.signTransaction(PrivateKey @@ privateKey, tx) } } @@ -96,7 +97,26 @@ final class NodeLanguageImpl(implicit system: ActorSystem, .singleRequest(request) .flatMap { response => response.entity.dataBytes - .runFold(AkkaByteString(""))(_ ++ _) + .runFold(AkkaByteString.empty)(_ ++ _) + .map { x => + val txr = transcode(Json @@ x.utf8String).to[Either[RpcError, TransactionResult]] + txr.left.map(_.message) + } + } + } + + def execute(data: ByteString, address: Address, endpoint: String): Future[Either[String, TransactionResult]] = { + val fromHex = bytes.byteString2hex(address) + val request = HttpRequest( + method = HttpMethods.POST, + uri = s"$endpoint/execute?from=$fromHex", + entity = HttpEntity(data.toByteArray) + ) + Http() + .singleRequest(request) + .flatMap { response => + response.entity.dataBytes + .runFold(AkkaByteString.empty)(_ ++ _) .map { x => val txr = transcode(Json @@ x.utf8String).to[Either[RpcError, TransactionResult]] txr.left.map(_.message) diff --git a/cli/src/main/scala/pravda/cli/languages/impl/RandomLanguageImpl.scala b/node-client/src/main/scala/pravda/node/client/impl/RandomLanguageImpl.scala similarity index 93% rename from cli/src/main/scala/pravda/cli/languages/impl/RandomLanguageImpl.scala rename to node-client/src/main/scala/pravda/node/client/impl/RandomLanguageImpl.scala index 7d8c93e1..96be0456 100644 --- a/cli/src/main/scala/pravda/cli/languages/impl/RandomLanguageImpl.scala +++ b/node-client/src/main/scala/pravda/node/client/impl/RandomLanguageImpl.scala @@ -15,13 +15,12 @@ * along with this program. If not, see . */ -package pravda.cli.languages - -package impl +package pravda.node.client.impl import java.security.SecureRandom import com.google.protobuf.ByteString +import pravda.node.client.RandomLanguage import scala.concurrent.Future diff --git a/cli/src/main/scala/pravda/cli/languages/impl/VmLanguageImpl.scala b/node-client/src/main/scala/pravda/node/client/impl/VmLanguageImpl.scala similarity index 96% rename from cli/src/main/scala/pravda/cli/languages/impl/VmLanguageImpl.scala rename to node-client/src/main/scala/pravda/node/client/impl/VmLanguageImpl.scala index c889f5e9..0daaecf4 100644 --- a/cli/src/main/scala/pravda/cli/languages/impl/VmLanguageImpl.scala +++ b/node-client/src/main/scala/pravda/node/client/impl/VmLanguageImpl.scala @@ -15,12 +15,11 @@ * along with this program. If not, see . */ -package pravda.cli.languages - -package impl +package pravda.node.client.impl import com.google.protobuf.ByteString import pravda.common.domain.Address +import pravda.node.client.VmLanguage import pravda.node.data.common.TransactionId import pravda.node.db.DB import pravda.node.servers diff --git a/cli/src/test/scala/pravda/cli/languages/IoLanguageStub.scala b/node-client/src/test/scala/pravda/node/client/IoLanguageStub.scala similarity index 98% rename from cli/src/test/scala/pravda/cli/languages/IoLanguageStub.scala rename to node-client/src/test/scala/pravda/node/client/IoLanguageStub.scala index c59f1c4d..e4243526 100644 --- a/cli/src/test/scala/pravda/cli/languages/IoLanguageStub.scala +++ b/node-client/src/test/scala/pravda/node/client/IoLanguageStub.scala @@ -1,4 +1,4 @@ -package pravda.cli.languages +package pravda.node.client import java.nio.charset.StandardCharsets diff --git a/cli/src/test/scala/pravda/cli/languages/NodeLanguageStub.scala b/node-client/src/test/scala/pravda/node/client/NodeLanguageStub.scala similarity index 88% rename from cli/src/test/scala/pravda/cli/languages/NodeLanguageStub.scala rename to node-client/src/test/scala/pravda/node/client/NodeLanguageStub.scala index 5d68698a..056ac1e9 100644 --- a/cli/src/test/scala/pravda/cli/languages/NodeLanguageStub.scala +++ b/node-client/src/test/scala/pravda/node/client/NodeLanguageStub.scala @@ -1,4 +1,4 @@ -package pravda.cli.languages +package pravda.node.client import cats.Id import com.google.protobuf.ByteString @@ -17,4 +17,5 @@ class NodeLanguageStub(result: Either[String, TransactionResult]) extends NodeLa wattPrice: NativeCoin, wattPayer: Option[Address], data: ByteString): Id[Either[String, TransactionResult]] = result + def execute(data: ByteString, address: Address, endpoint: String) = result } diff --git a/cli/src/test/scala/pravda/cli/languages/PredictableRandomLanguage.scala b/node-client/src/test/scala/pravda/node/client/PredictableRandomLanguage.scala similarity index 92% rename from cli/src/test/scala/pravda/cli/languages/PredictableRandomLanguage.scala rename to node-client/src/test/scala/pravda/node/client/PredictableRandomLanguage.scala index 8ea97ef2..ef0d5b67 100644 --- a/cli/src/test/scala/pravda/cli/languages/PredictableRandomLanguage.scala +++ b/node-client/src/test/scala/pravda/node/client/PredictableRandomLanguage.scala @@ -1,4 +1,4 @@ -package pravda.cli.languages +package pravda.node.client import cats.Id import com.google.protobuf.ByteString diff --git a/node-db/src/main/scala/pravda/node/db/DB.scala b/node-db/src/main/scala/pravda/node/db/DB.scala index a5fc667e..a232033c 100644 --- a/node-db/src/main/scala/pravda/node/db/DB.scala +++ b/node-db/src/main/scala/pravda/node/db/DB.scala @@ -29,7 +29,7 @@ import scala.collection.concurrent.TrieMap import scala.collection.mutable.ListBuffer import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future -import scala.util.{Failure, Success, Try} +import scala.util.control.NonFatal object DB { @@ -58,14 +58,33 @@ class DB( db.close() } - private def tryCloseable[A <: AutoCloseable, B](resource: A)(block: A => B): B = { - Try(block(resource)) match { - case Success(result) => - resource.close() - result - case Failure(e) => + // Resource management by @dkomanov + // https://medium.com/@dkomanov/scala-try-with-resources-735baad0fd7d + // TODO should be totally rewritten with akka streams. + def tryClosable[T <: AutoCloseable, V](r: => T)(f: T => V): V = { + def closeAndAddSuppressed(e: Throwable, resource: AutoCloseable): Unit = { + if (e != null) { + try { + resource.close() + } catch { + case NonFatal(suppressed) => + e.addSuppressed(suppressed) + } + } else { resource.close() + } + } + val resource: T = r + require(resource != null, "resource is null") + var exception: Throwable = null + try { + f(resource) + } catch { + case NonFatal(e) => + exception = e throw e + } finally { + closeAndAddSuppressed(exception, resource) } } @@ -132,7 +151,7 @@ class DB( val keys = operations.map(_.key).map(bArr).toSet exec(keys)( batchDiff(operations: _*), { - tryCloseable(db.createWriteBatch()) { batch => + tryClosable(db.createWriteBatch()) { batch => operations.foreach { case Operation.Delete(key) => batch.delete(key) @@ -261,12 +280,46 @@ class DB( def startsWithAs[V] = new StartsConstructor[V] + def startsWith(prefix: Array[Byte], offset: Array[Byte]): Stream[(Array[Byte], Array[Byte])] = { + var it: DBIterator = null + try { + it = db.iterator() + it.seek(prefix) + def next(): Stream[(Array[Byte], Array[Byte])] = { + try { + if (!it.hasNext) Stream.empty + else { + val record = it.peekNext() + val key = record.getKey + if (key.startsWith(prefix)) { + val value = record.getValue + Stream((key, value)) ++ next + } else { + it.close() + Stream.empty + } + } + } catch { + case e: Throwable => + if (it != null) + it.close() + throw e + } + } + next() + } catch { + case e: Throwable => + if (it != null) + it.close() + throw e + } + } + def startsWith[K](prefix: K, offset: K, count: Long)(implicit keyWriter: KeyWriter[K]): Future[List[Result]] = Future { val prefixBytes = keyWriter.toBytes(prefix) val offsetBytes = keyWriter.toBytes(offset) - tryCloseable(db.iterator()) { it => - val it = db.iterator() + tryClosable(db.iterator()) { it => it.seek(offsetBytes) val res = ListBuffer.empty[Result] while (res.length.toLong < count && it.hasNext && it.peekNext.getKey.startsWith(prefixBytes)) { @@ -318,7 +371,7 @@ class DB( private def syncCalcHash: Array[Byte] = { - tryCloseable(db.iterator()) { it => + tryClosable(db.iterator()) { it => it.seekToFirst() var hashSum = zeroHash diff --git a/node/src/main/scala/pravda/node/clients/AbciClient.scala b/node/src/main/scala/pravda/node/clients/AbciClient.scala index 7dc4ff3f..65ad5b16 100644 --- a/node/src/main/scala/pravda/node/clients/AbciClient.scala +++ b/node/src/main/scala/pravda/node/clients/AbciClient.scala @@ -29,7 +29,7 @@ import pravda.node.data.common.TransactionId import pravda.node.data.cryptography import pravda.node.data.cryptography.PrivateKey import pravda.node.data.serialization._ -import pravda.node.data.serialization.bson._ +import pravda.node.data.serialization.bjson._ import pravda.node.data.serialization.json._ import pravda.common.bytes._ import pravda.common.domain.{Address, NativeCoin} @@ -97,7 +97,7 @@ class AbciClient(port: Int)(implicit case Some(error) => throw RpcException(error) case None => - transcode(Bson @@ txResponse.result.get.tx.toByteArray).to[SignedTransaction] + transcode(BJson @@ txResponse.result.get.tx.toByteArray).to[SignedTransaction] } } case HttpResponse(code, _, entity, _) => @@ -118,7 +118,7 @@ class AbciClient(port: Int)(implicit def broadcastTransaction(tx: SignedTransaction, mode: String = "commit"): Future[ErrorOrExecInfo] = { - val bytes = transcode(tx).to[Bson] + val bytes = transcode(tx).to[BJson] broadcastBytes(bytes, mode) } @@ -131,7 +131,7 @@ class AbciClient(port: Int)(implicit val unsignedTx = Transaction.UnsignedTransaction(from, data, wattLimit, wattPrice, None, Random.nextInt()) val tx = cryptography.signTransaction(privateKey, unsignedTx) - val bytes = transcode(tx).to[Bson] + val bytes = transcode(tx).to[BJson] broadcastBytes(bytes, mode) } } diff --git a/node/src/main/scala/pravda/node/data/PravdaConfig.scala b/node/src/main/scala/pravda/node/data/PravdaConfig.scala index c5ceafc6..bca0ee4d 100644 --- a/node/src/main/scala/pravda/node/data/PravdaConfig.scala +++ b/node/src/main/scala/pravda/node/data/PravdaConfig.scala @@ -36,8 +36,7 @@ object PravdaConfig { time: String, chainId: String, appHash: String, - validators: Seq[GenesisValidator], - distribution: Boolean + validators: Seq[GenesisValidator] ) final case class Validator( privateKey: PrivateKey, @@ -46,12 +45,12 @@ object PravdaConfig { final case class GenesisValidator( publicKey: CryptoKey, - power: Int, + power: String, name: String ) final case class CryptoKey( `type`: String, - data: String + value: String ) final case class HttpConfig( host: String, diff --git a/node/src/main/scala/pravda/node/data/common.scala b/node/src/main/scala/pravda/node/data/common.scala index 158b4d71..a0e78804 100644 --- a/node/src/main/scala/pravda/node/data/common.scala +++ b/node/src/main/scala/pravda/node/data/common.scala @@ -27,7 +27,7 @@ import supertagged.TaggedType object common { /** - * Ripemd160 hash of BSON representation of signed transaction + * Sha3 hash of BSON representation of signed transaction */ object TransactionId extends TaggedType[ByteString] { diff --git a/node/src/main/scala/pravda/node/data/cryptography.scala b/node/src/main/scala/pravda/node/data/cryptography.scala index d07f347d..16f16435 100644 --- a/node/src/main/scala/pravda/node/data/cryptography.scala +++ b/node/src/main/scala/pravda/node/data/cryptography.scala @@ -24,8 +24,9 @@ import javax.crypto.{BadPaddingException, Cipher, SecretKeyFactory} import com.google.protobuf.ByteString import pravda.common.contrib.ed25519 import pravda.node.data.blockchain.Transaction +import pravda.node.data.serialization.json._ +import pravda.node.data.serialization.bjson._ import pravda.node.data.serialization._ -import pravda.node.data.serialization.bson._ import pravda.common.bytes._ import pravda.common.domain.Address import supertagged.TaggedType @@ -61,42 +62,17 @@ object cryptography { } } -// object SecurePasswordHash { -// -// val hasher: String => String = -// forPassword _ andThen (_.mkString) -// -// def forPassword(password: String): SecurePasswordHash = { -// val algorithm = "PBKDF2WithHmacSHA256" -// val iterations = 20000 -// val random = new SecureRandom() -// val salt = new Array[Byte](8) -// -// random.nextBytes(salt) -// -// val spec = new PBEKeySpec(password.toCharArray, salt, iterations) -// val f = SecretKeyFactory.getInstance(algorithm) -// -// SecurePasswordHash( -// passwordAlgorithm = algorithm, -// passwordIterations = iterations, -// passwordSalt = ByteString.copyFrom(salt), -// passwordHash = ByteString.copyFrom(f.generateSecret(spec).getEncoded) -// ) -// } -// } - def signTransaction(privateKey: PrivateKey, tx: UnsignedTransaction): SignedTransaction = signTransaction(privateKey.toByteArray, tx) def addWattPayerSignature(privateKey: PrivateKey, tx: SignedTransaction): SignedTransaction = { - val message = transcode(tx.forSignature).to[Bson] + val message = transcode(tx.forSignature).to[BJson] val signature = ed25519.sign(privateKey.toByteArray, message) tx.copy(wattPayerSignature = Some(ByteString.copyFrom(signature))) } private def signTransaction(privateKey: Array[Byte], tx: UnsignedTransaction): SignedTransaction = { - val message = transcode(tx.forSignature).to[Bson] + val message = transcode(tx.forSignature).to[BJson] val signature = ed25519.sign(privateKey, message) SignedTransaction( @@ -127,7 +103,7 @@ object cryptography { ) val pubKey = tx.from.toByteArray - val message = transcode(tx.forSignature).to[Bson] + val message = transcode(tx.forSignature).to[BJson] val signature = tx.signature.toByteArray if (ed25519.verify(pubKey, message, signature)) { (tx.wattPayer, tx.wattPayerSignature) match { diff --git a/node/src/main/scala/pravda/node/data/serialization/TethysInstances.scala b/node/src/main/scala/pravda/node/data/serialization/TethysInstances.scala index 5285b91b..17d2fb8c 100644 --- a/node/src/main/scala/pravda/node/data/serialization/TethysInstances.scala +++ b/node/src/main/scala/pravda/node/data/serialization/TethysInstances.scala @@ -19,9 +19,10 @@ package pravda.node.data.serialization import com.google.protobuf.ByteString import fastparse.utils.Base64 +import pravda.common.domain.{Address, NativeCoin} import pravda.node.clients.AbciClient._ import pravda.node.data.PravdaConfig -import pravda.node.data.common.{ApplicationStateInfo, CoinDistributionMember} +import pravda.node.data.common.{ApplicationStateInfo, CoinDistributionMember, TransactionId} import pravda.node.servers.{Abci, ApiRoute} import tethys._ import tethys.derivation.builder._ @@ -29,8 +30,17 @@ import tethys.derivation.semiauto._ import tethys.jackson.jacksonTokenIteratorProducer import tethys.jackson.pretty.prettyJacksonTokenWriterProducer import pravda.common.json._ +import pravda.node.data.blockchain.Transaction.SignedTransaction +import pravda.node.data.blockchain.TransactionData +import pravda.node.servers.Abci.StoredProgram +import pravda.vm.Effect +import pravda.common.json._ +import pravda.node.data.cryptography.EncryptedPrivateKey +import pravda.node.data.domain.Wallet import pravda.vm.json._ +import pravda.common.bytes.{byteString2hex, hex2byteString} + trait TethysInstances { //---------------------------------------------------------------------- // Config RWs for tethys @@ -185,4 +195,64 @@ trait TethysInstances { implicit def tethysJsonDecoder[T: JsonReader]: Transcoder[Json, T] = _.jsonAs[T].fold(throw _, identity) + + //--------------------------------------------------------------------------- + // Hack for BJson + //--------------------------------------------------------------------------- + + implicit val signedTransactionReader: JsonReader[SignedTransaction] = + jsonReader[SignedTransaction] + + implicit val signedTransactionWriter: JsonWriter[SignedTransaction] = + jsonWriter[SignedTransaction] + + implicit val storedProgramReader: JsonReader[StoredProgram] = + jsonReader[StoredProgram] + + implicit val storedProgramWriter: JsonWriter[StoredProgram] = + jsonWriter[StoredProgram] + + implicit val forSignatureReaderReader + : JsonReader[(Address, TransactionData, Long, NativeCoin, Int, Option[Address])] = + JsonReader.builder + .addField[Address]("a") + .addField[TransactionData]("td") + .addField[Long]("l") + .addField[NativeCoin]("nc") + .addField[Int]("i") + .addField[Option[Address]]("oa") + .buildReader((a, td, l, nc, i, oa) => (a, td, l, nc, i, oa)) + + implicit val forSignatureReaderWriter + : JsonWriter[(Address, TransactionData, Long, NativeCoin, Int, Option[Address])] = + JsonWriter + .obj[(Address, TransactionData, Long, NativeCoin, Int, Option[Address])] + .addField[Address]("a")(_._1) + .addField[TransactionData]("td")(_._2) + .addField[Long]("l")(_._3) + .addField[NativeCoin]("nc")(_._4) + .addField[Int]("i")(_._5) + .addField[Option[Address]]("oa")(_._6) + + implicit val epkReader: JsonReader[EncryptedPrivateKey] = + jsonReader[EncryptedPrivateKey] + + implicit val epkWriter: JsonWriter[EncryptedPrivateKey] = + jsonWriter[EncryptedPrivateKey] + + implicit val walletReader: JsonReader[Wallet] = + jsonReader[Wallet] + + implicit val walletWriter: JsonWriter[Wallet] = + jsonWriter[Wallet] + + implicit val tIdKeySupport: MapKeySupport[TransactionId] = new MapKeySupport[TransactionId] { + def show(x: TransactionId): String = byteString2hex(x) + def mk(x: String): TransactionId = TransactionId @@ hex2byteString(x) + } + + implicit val mtiseReader: JsonReader[Map[TransactionId, Seq[Effect]]] = mapReader + + implicit val mtiseWriter: JsonWriter[Map[TransactionId, Seq[Effect]]] = mapWriter + } diff --git a/node/src/main/scala/pravda/node/data/serialization/bjson.scala b/node/src/main/scala/pravda/node/data/serialization/bjson.scala new file mode 100644 index 00000000..a4465871 --- /dev/null +++ b/node/src/main/scala/pravda/node/data/serialization/bjson.scala @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2018 Expload.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package pravda.node.data.serialization + +import java.nio.charset.StandardCharsets + +import tethys._ +import tethys.jackson.jacksonTokenIteratorProducer +import tethys.jackson.jacksonTokenWriterProducer + +object bjson extends BJsonTranscoder + +trait BJsonTranscoder { + + type BJsonEncoder[T] = Transcoder[T, BJson] + type BJsonDecoder[T] = Transcoder[BJson, T] + + implicit def bjsonEncoder[T: JsonWriter]: BJsonEncoder[T] = + t => BJson @@ t.asJson.getBytes(StandardCharsets.UTF_8) + + implicit def bjsonDecoder[T: JsonReader]: BJsonDecoder[T] = + t => new String(t, StandardCharsets.UTF_8).jsonAs[T].fold(throw _, identity) +} diff --git a/node/src/main/scala/pravda/node/data/serialization/boopick.scala b/node/src/main/scala/pravda/node/data/serialization/boopick.scala deleted file mode 100644 index 7e8823ee..00000000 --- a/node/src/main/scala/pravda/node/data/serialization/boopick.scala +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (C) 2018 Expload.com - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package pravda.node.data.serialization - -import java.nio.ByteBuffer - -import boopickle.Default._ -import boopickle.{CompositePickler, Pickler} -import com.google.protobuf.{ByteString => PbByteString} -import pravda.node.data.blockchain.Transaction -import supertagged.{@@, lifterF} -import akka.util.{ByteString => AkkaByteString} - -object boopick { - - //--------------------------------------------------------------------------- - // Misc picklers - //--------------------------------------------------------------------------- - - /** This pickler allow to use protocol buffers' ByteString inside case classes */ - implicit val pbByteStringPickler: Pickler[PbByteString] = - transformPickler[PbByteString, Array[Byte]](byteArray => PbByteString.copyFrom(byteArray))(byteString => - byteString.toByteArray) - - /** Allow to use tagged primitives inside case classes */ - implicit def picklerLifter[T: Pickler, U]: Pickler[@@[T, U]] = - lifterF[Pickler].lift[T, U] - - //--------------------------------------------------------------------------- - // Simple picklers - //--------------------------------------------------------------------------- - - implicit val signedTransactionPickler: Pickler[Transaction.SignedTransaction] = - generatePickler[Transaction.SignedTransaction] - - implicit val unsignedTransactionPickler: Pickler[Transaction.UnsignedTransaction] = - generatePickler[Transaction.UnsignedTransaction] - - implicit val authorizedTransactionPickler: Pickler[Transaction.AuthorizedTransaction] = - generatePickler[Transaction.AuthorizedTransaction] - - //--------------------------------------------------------------------------- - // Composite picklers - //--------------------------------------------------------------------------- - - implicit val transactionPickler: CompositePickler[Transaction] = - compositePickler[Transaction] - .addConcreteType[Transaction.SignedTransaction] - .addConcreteType[Transaction.UnsignedTransaction] - .addConcreteType[Transaction.AuthorizedTransaction] - - //--------------------------------------------------------------------------- - // Transcoding - //--------------------------------------------------------------------------- - - implicit def binaryEncoder[T: Pickler]: Transcoder[T, BooPickle] = { obj => - val buffer = Pickle.intoBytes(obj) - val bytes = Array.ofDim[Byte](buffer.remaining()) - buffer.get(bytes) - BooPickle(bytes) - } - - implicit def bpByteStringEncoder[T: Pickler]: Transcoder[T, PbByteString] = { value => - val buffer = Pickle.intoBytes(value) - PbByteString.copyFrom(buffer) - } - - implicit def binaryDecoder[T: Pickler]: Transcoder[BooPickle, T] = { binary => - val buffer = ByteBuffer.wrap(binary) - Unpickle[T].fromBytes(buffer) - } - - implicit def akkaByteStringDecoder[T: Pickler]: Transcoder[AkkaByteString, T] = { binary => - val buffer = binary.toByteBuffer - Unpickle[T].fromBytes(buffer) - } - - implicit def bpByteStringDecoder[T: Pickler]: Transcoder[PbByteString, T] = { binary => - val buffer = ByteBuffer.wrap(binary.toByteArray) - Unpickle[T].fromBytes(buffer) - } -} diff --git a/node/src/main/scala/pravda/node/data/serialization/bson.scala b/node/src/main/scala/pravda/node/data/serialization/bson.scala deleted file mode 100644 index 4e47e3de..00000000 --- a/node/src/main/scala/pravda/node/data/serialization/bson.scala +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (C) 2018 Expload.com - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package pravda.node.data.serialization - -import com.google.protobuf.ByteString -import pravda.vm.Data -import reactivemongo.bson.buffer.{ArrayBSONBuffer, ArrayReadableBuffer} -import reactivemongo.bson.{BSONDocument, BSONValue} -import supertagged.{Tagged, lifterF} - -import scala.util.Success - -object bson extends BsonTranscoder - -trait BsonTranscoder { - // --- MAIN PART ------------- - type BsonEncoder[T] = Transcoder[T, Bson] - type BsonDecoder[T] = Transcoder[Bson, T] - - implicit def bsonEncoder[T](implicit pickle: bsonpickle.default.Writer[T]): BsonEncoder[T] = (value: T) => { - val pickled = pickle.write(value) - val doc = pickled match { - case d: BSONDocument => d - case o => BSONDocument("" -> o) - } - val buffer = new ArrayBSONBuffer() - BSONDocument.write(doc, buffer) - Bson @@ buffer.array - } - - implicit def bsonDecoder[T](implicit pickle: bsonpickle.default.Reader[T]): BsonDecoder[T] = (bson: Bson) => { - val doc = BSONDocument.read(ArrayReadableBuffer(bson)) - val value = if (doc.elements.head.name.isEmpty) doc.elements.head.value else doc - pickle.read(value) - } - //----------------------- - - import bsonpickle.default.{Reader => BsonReader, Writer => BsonWriter} - - // Tagged types - - implicit def taggedReaderLifter[T: BsonReader, U]: BsonReader[Tagged[T, U]] = - lifterF[BsonReader].lift[T, U] - - implicit def taggedWriterLifter[T: BsonWriter, U]: BsonWriter[Tagged[T, U]] = - lifterF[BsonWriter].lift[T, U] - - // Data - - implicit def dataWriter(implicit arrayWriter: BsonWriter[Array[Byte]]): BsonWriter[Data] = - new BsonWriter[Data] { - override def write0: Data => BSONValue = { x => - arrayWriter.write0(x.toByteString.toByteArray) - } - } - - implicit val dataReader: BsonReader[Data] = - new BsonReader[Data] { - private val arrayReader = implicitly[BsonReader[Array[Byte]]] - override def read0: PartialFunction[BSONValue, Data] = { - case x if arrayReader.read0.isDefinedAt(x) => Data.fromBytes(arrayReader.read0(x)) - } - } - - // Protobuf - - // fomkin: I dont get why this reader is not derived by autoderivation - // Looks like it some kind of bug in implicit macros. - implicit val mapRefDataReader: BsonReader[Map[Data.Primitive.Ref, Data]] = { - new BsonReader[Map[Data.Primitive.Ref, Data]] { - override def read0: PartialFunction[BSONValue, Map[Data.Primitive.Ref, Data]] = { - case BSONDocument(xs) => - xs.collect { - case Success(x) => - val k = Data.Primitive.Ref(x.name.substring(x.name.indexOf(':') + 1).toInt) - val v = dataReader.read(x.value) - k -> v - }.toMap - } - } - } - - implicit def protoWriter(implicit arrayWriter: BsonWriter[Array[Byte]]): BsonWriter[ByteString] = - new BsonWriter[ByteString] { - override def write0: ByteString => BSONValue = { x => - arrayWriter.write0(x.toByteArray) - } - } - - implicit def protoReader(implicit arrayReader: BsonReader[Array[Byte]]): BsonReader[ByteString] = - new BsonReader[ByteString] { - override def read0: PartialFunction[BSONValue, ByteString] = { - case x if arrayReader.read0.isDefinedAt(x) => ByteString.copyFrom(arrayReader.read0(x)) - } - } - -} diff --git a/node/src/main/scala/pravda/node/data/serialization/config.scala b/node/src/main/scala/pravda/node/data/serialization/config.scala index c789ba93..e396de58 100644 --- a/node/src/main/scala/pravda/node/data/serialization/config.scala +++ b/node/src/main/scala/pravda/node/data/serialization/config.scala @@ -18,9 +18,11 @@ package pravda.node.data.serialization import pravda.common.domain.{Address, NativeCoin} +import pravda.common.{bytes => byteUtils} import pravda.node.data.PravdaConfig.{CryptoKey, GenesisValidator} import pravda.node.data.common.CoinDistributionMember import pravda.node.data.cryptography.PrivateKey +import pravda.node.tendermint import pureconfig.ConfigReader /** @@ -46,10 +48,11 @@ object config { .filter(_.nonEmpty) .map { s => val Array(name, power, key) = s.split(":") + val keyBase64 = byteUtils.hexToBase64(key) GenesisValidator( name = name, - power = power.toInt, - publicKey = CryptoKey("ed25519", key) + power = power, + publicKey = CryptoKey(tendermint.PubKeyEd25519, keyBase64) ) } } diff --git a/node/src/main/scala/pravda/node/data/serialization/package.scala b/node/src/main/scala/pravda/node/data/serialization/package.scala index e1a76b63..d50a117f 100644 --- a/node/src/main/scala/pravda/node/data/serialization/package.scala +++ b/node/src/main/scala/pravda/node/data/serialization/package.scala @@ -27,11 +27,8 @@ package object serialization { object Json extends TaggedType[String] type Json = Json.Type - object BooPickle extends TaggedType[Array[Byte]] - type BooPickle = BooPickle.Type - - object Bson extends TaggedType[Array[Byte]] - type Bson = Bson.Type + object BJson extends TaggedType[Array[Byte]] + type BJson = BJson.Type object Composite extends TaggedType[Array[Byte]] type Composite = Composite.Type diff --git a/node/src/main/scala/pravda/node/launcher.scala b/node/src/main/scala/pravda/node/launcher.scala index d0fa1540..8345260d 100644 --- a/node/src/main/scala/pravda/node/launcher.scala +++ b/node/src/main/scala/pravda/node/launcher.scala @@ -51,7 +51,7 @@ object launcher extends App { ) val abci = new Abci(applicationStateDb, abciClient, pravdaConfig.coinDistribution) - val server = Server( + val abciServer = Server( cfg = Server.Config( connectionMethod = if (pravdaConfig.tendermint.useUnixDomainSocket) { val path = new File(pravdaConfig.dataDirectory, "abci.sock").getAbsolutePath @@ -63,25 +63,27 @@ object launcher extends App { api = abci ) - val httpServer = { - val apiRoute = new ApiRoute(abciClient, applicationStateDb, abci) - val guiRoute = new GuiRoute(abciClient, applicationStateDb) - HttpServer.start(pravdaConfig.http, apiRoute.route, guiRoute.route) - } - - val tendermintNode = Await.result(tendermint.run(pravdaConfig), 10.seconds) + val apiRoute = new ApiRoute(abciClient, applicationStateDb, abci) + val guiRoute = new GuiRoute(abciClient, applicationStateDb) - server.start() + val res = for { + h <- HttpServer.start(pravdaConfig.http, apiRoute.route, guiRoute.route) + a <- abciServer.start() + t <- tendermint.run(pravdaConfig) + } yield (h, a, t) println("Tendermint node started") sys.addShutdownHook { + val (httpServer, abciServer, tendermintNode) = Await.result(res, 10.seconds) + print("Shutting down tendermint node...") tendermintNode.destroy() println(s"${Console.GREEN} done${Console.RESET}") print("Shutting down API server...") - Await.result(httpServer.flatMap(_.unbind()), 10.second) + Await.result(httpServer.unbind(), 10.seconds) + Await.result(abciServer.unbind(), 10.seconds) system.terminate() println(s"${Console.GREEN} done${Console.RESET}") diff --git a/node/src/main/scala/pravda/node/persistence/BlockChainStore.scala b/node/src/main/scala/pravda/node/persistence/BlockChainStore.scala index ed3a8b9c..9b232390 100644 --- a/node/src/main/scala/pravda/node/persistence/BlockChainStore.scala +++ b/node/src/main/scala/pravda/node/persistence/BlockChainStore.scala @@ -20,6 +20,7 @@ package pravda.node.persistence import pravda.common.domain.{Address, NativeCoin} import pravda.node.db.DB import pravda.node.persistence.implicits._ +import pravda.node.data.serialization.json._ object BlockChainStore { def balanceEntry(db: DB): Entry[Address, NativeCoin] = Entry[Address, NativeCoin](db, "balance") diff --git a/node/src/main/scala/pravda/node/persistence/DbPath.scala b/node/src/main/scala/pravda/node/persistence/DbPath.scala index 9da77f11..85c4ae4f 100644 --- a/node/src/main/scala/pravda/node/persistence/DbPath.scala +++ b/node/src/main/scala/pravda/node/persistence/DbPath.scala @@ -17,12 +17,13 @@ package pravda.node.persistence -import pravda.node.data.serialization.bson.{BsonDecoder, BsonEncoder} -import pravda.node.data.serialization.{Bson, transcode} +import pravda.node.data.serialization.{BJson, transcode} import pravda.node.db.{DB, Operation} import scala.collection.mutable import pravda.common.{bytes => byteUtils} +import pravda.node.data.serialization.bjson._ +import tethys.{JsonReader, JsonWriter} trait DbPath { @@ -30,14 +31,14 @@ trait DbPath { def :+(suffix: String): DbPath - def getAs[V: BsonDecoder](suffix: String): Option[V] = - getRawBytes(suffix).map(arr => transcode(Bson @@ arr).to[V]) + def getAs[V: JsonReader](suffix: String): Option[V] = + getRawBytes(suffix).map(arr => transcode[BJson](BJson @@ arr).to[V]) def getRawBytes(suffix: String): Option[Array[Byte]] - def put[V: BsonEncoder](suffix: String, value: V): Option[Array[Byte]] = { - val bsonValue: Array[Byte] = transcode(value).to[Bson] - putRawBytes(suffix, bsonValue) + def put[V: JsonWriter](suffix: String, value: V): Option[Array[Byte]] = { + val jsonBytes: Array[Byte] = transcode(value).to[BJson] + putRawBytes(suffix, jsonBytes) } def putRawBytes(suffix: String, value: Array[Byte]): Option[Array[Byte]] diff --git a/node/src/main/scala/pravda/node/persistence/NodeStore.scala b/node/src/main/scala/pravda/node/persistence/NodeStore.scala index 9f8a909f..48406f69 100644 --- a/node/src/main/scala/pravda/node/persistence/NodeStore.scala +++ b/node/src/main/scala/pravda/node/persistence/NodeStore.scala @@ -23,6 +23,7 @@ import scala.concurrent.Future // Implicits import pravda.node.persistence.implicits._ +import pravda.node.data.serialization.json._ object NodeStore { def apply(path: String): NodeStore = new NodeStore(path) diff --git a/node/src/main/scala/pravda/node/persistence/implicits.scala b/node/src/main/scala/pravda/node/persistence/implicits.scala index 6ad88b05..86ba46b7 100644 --- a/node/src/main/scala/pravda/node/persistence/implicits.scala +++ b/node/src/main/scala/pravda/node/persistence/implicits.scala @@ -20,21 +20,16 @@ package pravda.node package persistence import pravda.node.db.serialyzer.{KeyWriter, ValueReader, ValueWriter} -import pravda.node.data.serialization.{Bson, BsonTranscoder, CompositeTranscoder} import data.serialization._ -object implicits extends BsonTranscoder with CompositeTranscoder { +object implicits extends BJsonTranscoder with CompositeTranscoder { - implicit def keyWriter[T: CompositeEncoder]: KeyWriter[T] = new KeyWriter[T] { - override def toBytes(value: T): Array[Byte] = transcode(value).to[Composite] - } + implicit def keyWriter[T: CompositeEncoder]: KeyWriter[T] = (value: T) => transcode(value).to[Composite] - implicit def valueReader[T: BsonDecoder]: ValueReader[T] = new ValueReader[T] { - override def fromBytes(array: Array[Byte]): T = transcode(Bson @@ array).to[T] - } + implicit def valueReader[T](implicit t: Transcoder[BJson, T]): ValueReader[T] = + (array: Array[Byte]) => transcode(BJson @@ array).to[T] - implicit def valueWriter[T: BsonEncoder]: ValueWriter[T] = new ValueWriter[T] { - override def toBytes(value: T): Array[Byte] = transcode(value).to[Bson] - } + implicit def valueWriter[T](implicit t: Transcoder[T, BJson]): ValueWriter[T] = + (value: T) => transcode(value).to[BJson] } diff --git a/node/src/main/scala/pravda/node/servers/Abci.scala b/node/src/main/scala/pravda/node/servers/Abci.scala index 0920b0b2..83c075aa 100644 --- a/node/src/main/scala/pravda/node/servers/Abci.scala +++ b/node/src/main/scala/pravda/node/servers/Abci.scala @@ -29,7 +29,7 @@ import pravda.node.data.blockchain.Transaction.{AuthorizedTransaction, SignedTra import pravda.node.data.common.{ApplicationStateInfo, CoinDistributionMember, TransactionId} import pravda.node.data.cryptography import pravda.node.data.serialization._ -import pravda.node.data.serialization.bson._ +import pravda.node.data.serialization.bjson._ import pravda.node.data.serialization.json._ import pravda.node.db.{DB, Operation} import pravda.node.persistence.BlockChainStore.balanceEntry @@ -65,7 +65,7 @@ class Abci(applicationStateDb: DB, abciClient: AbciClient, initialDistribution: def initChain(request: RequestInitChain): Future[ResponseInitChain] = { val initValidators = request.validators.toVector - .map(x => tendermint.unpackAddress(x.pubKey)) + .map(x => tendermint.unpackAddress(x.getPubKey.data)) for { _ <- FileStore @@ -80,16 +80,17 @@ class Abci(applicationStateDb: DB, abciClient: AbciClient, initialDistribution: def beginBlock(request: RequestBeginBlock): Future[ResponseBeginBlock] = { consensusEnv.clear() + val malicious = request.byzantineValidators.map(x => tendermint.unpackAddress(x.getValidator.address)) + val absent = request.getLastCommitInfo.votes.collect { + case VoteInfo(validator, signedLastBlock) if !signedLastBlock => + validator.map(v => tendermint.unpackAddress(v.address)) + }.flatten - val malicious = request.byzantineValidators.map(x => tendermint.unpackAddress(x.pubKey)) - val absent = request.absentValidators FileStore .readApplicationStateInfoAsync() .map { maybeInfo => val info = maybeInfo.getOrElse(ApplicationStateInfo(0, ByteString.EMPTY, Vector.empty[Address])) - validators = info.validators.zipWithIndex.collect { - case (address, i) if !malicious.contains(address) && !absent.contains(i) => address - } + validators = info.validators.filter(address => !malicious.contains(address) && !absent.contains(address)) } .map(_ => ResponseBeginBlock()) } @@ -127,7 +128,7 @@ class Abci(applicationStateDb: DB, abciClient: AbciClient, initialDistribution: val remaining = tx.wattLimit - total ep.accrue(wattPayer, NativeCoin(tx.wattPrice * remaining)) ep.appendFee(NativeCoin(tx.wattPrice * total)) - TransactionResult(execResult, env.collectEffects) + TransactionResult(id, execResult, env.collectEffects) } } @@ -144,7 +145,7 @@ class Abci(applicationStateDb: DB, abciClient: AbciClient, initialDistribution: result: (Int, String) => R): Future[R] = { val tid = TransactionId.forEncodedTransaction(encodedTransaction) - val `try` = Try(transcode(Bson @@ encodedTransaction.toByteArray).to[SignedTransaction]) + val `try` = Try(transcode(BJson @@ encodedTransaction.toByteArray).to[SignedTransaction]) .flatMap(verifySignedTx(_, tid, environmentProvider)) Future.successful { @@ -201,6 +202,7 @@ class Abci(applicationStateDb: DB, abciClient: AbciClient, initialDistribution: object Abci { final case class TransactionResult( + transactionId: TransactionId, executionResult: ExecutionResult, effects: Seq[Effect] ) @@ -227,7 +229,6 @@ object Abci { private var fee = NativeCoin.zero private val operations = mutable.Buffer.empty[Operation] private val effectsMap = mutable.Buffer.empty[(TransactionId, mutable.Buffer[Effect])] - private val events = mutable.Buffer.empty[Effect.Event] private val cache = mutable.Map.empty[String, Option[Array[Byte]]] private lazy val blockProgramsPath = new CachedDbPath(new PureDbPath(db, "program"), cache, operations) @@ -259,7 +260,6 @@ object Abci { operations ++= transactionOperations cache ++= transactionCache effects ++= transactionEffects - events ++= transactionEffects.collect { case ev: Effect.Event => ev } } import Effect._ @@ -350,6 +350,18 @@ object Abci { } def collectEffects: Seq[Effect] = transactionEffects + + def chainHeight: Long = { + FileStore + .readApplicationStateInfo() + .fold(throw ThrowableVmError(Error.NoInfoAboutAppState))(_.blockHeight) + } + + def lastBlockHash: ByteString = { + FileStore + .readApplicationStateInfo() + .fold(throw ThrowableVmError(Error.NoInfoAboutAppState))(_.appHash) + } } def appendFee(coins: NativeCoin): Unit = { @@ -360,7 +372,6 @@ object Abci { def clear(): Unit = { operations.clear() effectsMap.clear() - events.clear() cache.clear() fee = NativeCoin.zero } @@ -386,7 +397,6 @@ object Abci { } def commit(height: Long, validators: Vector[Address]): Unit = { - // Share fee val share = NativeCoin @@ (fee / validators.length) val remainder = NativeCoin @@ (fee % validators.length) @@ -400,20 +410,26 @@ object Abci { blockEffectsPath.put(byteUtils.bytes2hex(byteUtils.longToBytes(height)), data) } - events + effectsMap + .flatMap { + case (tx, buffer) => + buffer collect { + case event: Effect.Event => + tx -> event + } + } .groupBy { - case Effect.Event(address, name, data) => (address, name) + case (_, Effect.Event(address, name, _)) => (address, name) } .foreach { case ((address, name), evs) => val len = eventsPath.getAs[Long](eventKeyLength(address, name)).getOrElse(0L) evs.zipWithIndex.foreach { - case (Effect.Event(_, _, data), i) => - eventsPath.put(eventKeyOffset(address, name, len + i.toLong), data) + case ((tx, Effect.Event(_, _, data)), i) => + eventsPath.put(eventKeyOffset(address, name, len + i.toLong), (tx, data)) } eventsPath.put(eventKeyLength(address, name), len + evs.length.toLong) } - db.syncBatch(operations: _*) clear() } diff --git a/node/src/main/scala/pravda/node/servers/ApiRoute.scala b/node/src/main/scala/pravda/node/servers/ApiRoute.scala index bb1b141b..ae683a00 100644 --- a/node/src/main/scala/pravda/node/servers/ApiRoute.scala +++ b/node/src/main/scala/pravda/node/servers/ApiRoute.scala @@ -35,17 +35,20 @@ import pravda.node.data.blockchain.Transaction.{SignedTransaction, UnsignedTrans import pravda.node.data.blockchain.TransactionData import pravda.node.data.common.TransactionId import pravda.node.data.serialization.json._ -import pravda.node.data.serialization.{Bson, transcode} +import pravda.node.data.serialization.bjson._ +import tethys._ +import pravda.node.data.serialization._ import pravda.node.db.DB import pravda.node.persistence.BlockChainStore._ import pravda.node.persistence.Entry import pravda.node.servers.Abci.TransactionResult +import pravda.vm.impl.VmImpl import pravda.vm.{MarshalledData, ThrowableVmError} import scala.concurrent.ExecutionContext import scala.concurrent.duration._ import scala.language.postfixOps -import scala.util.{Failure, Random, Success} +import scala.util.{Failure, Random, Success, Try} /** * @param abci Direct access to transaction processing. Required by dry-run @@ -54,6 +57,8 @@ class ApiRoute(abciClient: AbciClient, db: DB, abci: Abci)(implicit executionCon import pravda.node.utils.AkkaHttpSpecials._ + type R = Either[RpcError, TransactionResult] + val hexUnmarshaller: Unmarshaller[String, ByteString] = Unmarshaller.strict(hex => bytes.hex2byteString(hex)) @@ -135,6 +140,33 @@ class ApiRoute(abciClient: AbciClient, db: DB, abci: Abci)(implicit executionCon } } } ~ + path("execute") { + parameters('from.as(hexUnmarshaller)) { from => + extractStrictEntity(1.second) { body => + val bde = new node.servers.Abci.BlockDependentEnvironment(db) + val program = bodyToTransactionData(body) + val wattLimit = Long.MaxValue + val transactionId = TransactionId.Empty + val vm = new VmImpl() + val env = bde.transactionEnvironment(Address @@ from, transactionId) + + val result = for { + execResult <- Try(vm.spawn(program, env, wattLimit)) + } yield TransactionResult(transactionId, execResult, env.collectEffects) + + result match { + case Success(x) => + complete(Right(x): R) + case Failure(e: ThrowableVmError) => + complete(Left(RpcError(-1, e.error.toString, "")): R) + case Failure(e: Abci.TransactionValidationException) => + complete(Left(RpcError(-1, e.getMessage, "")): R) + case Failure(e) => + failWith(e) + } + } + } + } ~ post { withoutRequestTimeout { path("broadcast") { @@ -188,26 +220,36 @@ class ApiRoute(abciClient: AbciClient, db: DB, abci: Abci)(implicit executionCon } ~ get { path("events") { - import pravda.node.data.serialization.bson._ parameters( ( - 'address.as(hexUnmarshaller), + 'program.as(hexUnmarshaller), 'name, + 'transactionId.as(hexUnmarshaller).?, 'offset.as(intUnmarshaller).?, 'count.as(intUnmarshaller).? - )) { (address, name, offsetO, countO) => - val offset = offsetO.getOrElse(0) - val count = countO.map(c => math.min(c, ApiRoute.MaxEventCount)).getOrElse(ApiRoute.MaxEventCount) - val f = db + )) { (address, name, maybeTransaction, maybeOffset, maybeCount) => + val offset = maybeOffset.getOrElse(0) + val count = maybeCount.fold(ApiRoute.MaxEventCount)(math.min(_, ApiRoute.MaxEventCount)) + val eventuallyResult = db .startsWith( bytes.stringToBytes(s"events:${eventKey(Address @@ address, name)}"), bytes.stringToBytes(s"events:${eventKeyOffset(Address @@ address, name, offset.toLong)}"), count.toLong ) - .map(_.map(r => transcode(Bson @@ r.bytes).to[MarshalledData])) + .map { records => + records.map(value => + transcode(BJson @@ value.bytes) + .to[(TransactionId, MarshalledData)]) + } - onSuccess(f) { res => - complete(res.zipWithIndex.map { case (d, i) => ApiRoute.EventItem(i + offset, d) }) + onSuccess(eventuallyResult) { result => + val items = { + val xs = result.zipWithIndex.map { + case ((tid, d), n) => ApiRoute.EventItem(n + offset, tid, d) + } + maybeTransaction.fold(xs)(tid => xs.filter(_.transactionId == tid)) + } + complete(items) } } } @@ -219,5 +261,5 @@ object ApiRoute { final val MaxEventCount = 1000 - final case class EventItem(offset: Int, data: MarshalledData) + final case class EventItem(offset: Int, transactionId: TransactionId, data: MarshalledData) } diff --git a/node/src/main/scala/pravda/node/servers/GuiRoute.scala b/node/src/main/scala/pravda/node/servers/GuiRoute.scala index 9c62acb9..7782cf66 100644 --- a/node/src/main/scala/pravda/node/servers/GuiRoute.scala +++ b/node/src/main/scala/pravda/node/servers/GuiRoute.scala @@ -38,7 +38,8 @@ import pravda.node.data.common.TransactionId import pravda.node.data.cryptography import pravda.node.data.cryptography.PrivateKey import pravda.node.data.serialization._ -import pravda.node.data.serialization.bson._ +import pravda.node.data.serialization.json._ +import pravda.node.data.serialization.bjson._ import pravda.node.db.DB import pravda.node.persistence.FileStore import pravda.node.utils @@ -428,7 +429,7 @@ class GuiRoute(abciClient: AbciClient, db: DB)(implicit system: ActorSystem, mat val key = s"effects:${byteUtils.bytes2hex(byteUtils.longToBytes(height))}" for { blockInfo <- OptionT(db.get(byteUtils.stringToBytes(key))).map(r => - transcode(Bson @@ r.bytes).to[Map[TransactionId, Seq[vm.Effect]]]) + transcode(BJson @@ r.bytes).to[Map[TransactionId, Seq[vm.Effect]]]) eventuallyTransaction = blockInfo.keys.map(tid => abciClient.readTransaction(tid).map(tx => tid -> tx)) transactions <- OptionT.liftF(Future.sequence(eventuallyTransaction)) } yield { diff --git a/node/src/main/scala/pravda/node/servers/HttpServer.scala b/node/src/main/scala/pravda/node/servers/HttpServer.scala index 15173343..36235715 100644 --- a/node/src/main/scala/pravda/node/servers/HttpServer.scala +++ b/node/src/main/scala/pravda/node/servers/HttpServer.scala @@ -36,7 +36,7 @@ object HttpServer { executionContext: ExecutionContextExecutor): Future[Http.ServerBinding] = { val route = pathPrefix("healthz") { complete("pravda node: I'm OK :)") - } ~ + } ~ path("version") { { complete(pravda.node.BuildInfo.version) } } ~ pathPrefix("api")(apiRoute) ~ pathPrefix("ui")(guiRoute) Http().bindAndHandle(route, config.host, config.port) andThen { diff --git a/node/src/main/scala/pravda/node/tendermint.scala b/node/src/main/scala/pravda/node/tendermint.scala index f6552fd9..473ffbe7 100644 --- a/node/src/main/scala/pravda/node/tendermint.scala +++ b/node/src/main/scala/pravda/node/tendermint.scala @@ -32,6 +32,9 @@ import pravda.common.{bytes => byteUtils} object tendermint { + val PubKeyEd25519 = "tendermint/PubKeyEd25519" + val PrivKeyEd25519 = "tendermint/PrivKeyEd25519" + private final val GoWireAddressHeader = ByteString.copyFrom(Array[Byte](0x01, 0x01, 0x20)) @@ -134,8 +137,8 @@ object tendermint { privValidatorFile.delete() config.validator foreach { validator => writeFile(privValidatorFile) { - val pubKey = byteUtils.byteString2hex(validator.address) - val privKey = byteUtils.byteString2hex(validator.privateKey) + val pubKey = byteUtils.byteStringToBase64(validator.address) + val privKey = byteUtils.byteStringToBase64(validator.privateKey) val address = { val withType = packAddress(validator.address) val hash = ripemd160.getHash(withType.toByteArray) @@ -145,16 +148,16 @@ object tendermint { |{ | "address": "$address", | "pub_key": { - | "type": "ed25519", - | "data": "$pubKey" + | "type": "$PubKeyEd25519", + | "value": "$pubKey" | }, - | "last_height": 0, - | "last_round": 0, + | "last_height": "0", + | "last_round": "0", | "last_step": 0, | "last_signature": null, | "priv_key": { - | "type": "ed25519", - | "data": "$privKey" + | "type": "$PrivKeyEd25519", + | "value": "$privKey" | } |} """.stripMargin diff --git a/node/src/main/scala/pravda/node/utils/AkkaHttpSpecials.scala b/node/src/main/scala/pravda/node/utils/AkkaHttpSpecials.scala index e8a988f3..91226e4b 100644 --- a/node/src/main/scala/pravda/node/utils/AkkaHttpSpecials.scala +++ b/node/src/main/scala/pravda/node/utils/AkkaHttpSpecials.scala @@ -46,13 +46,13 @@ object AkkaHttpSpecials extends PredefinedToResponseMarshallers { .contentType implicit def transcodingUnmarshaller[T](implicit jtc: Transcoder[Json, T], - btc: Transcoder[Bson, T], + btc: Transcoder[BJson, T], mat: ActorMaterializer): FromEntityUnmarshaller[T] = Unmarshaller { implicit ec => entity => entity.toStrict(5.seconds) map { case strict if strict.contentType.mediaType == MediaTypes.`application/octet-stream` => val bytesRaw = strict.data.toArray - transcode(Bson @@ bytesRaw).to[T] + transcode(BJson @@ bytesRaw).to[T] case strict if strict.contentType.mediaType == MediaTypes.`application/json` => val charset = strict.contentType.charsetOption .fold(StandardCharsets.UTF_8)(_.nioCharset()) diff --git a/node/src/test/scala/pravda/node/BsonTranscode.scala b/node/src/test/scala/pravda/node/BsonTranscode.scala index 3a0a93bb..9da8a704 100644 --- a/node/src/test/scala/pravda/node/BsonTranscode.scala +++ b/node/src/test/scala/pravda/node/BsonTranscode.scala @@ -4,16 +4,18 @@ import com.google.protobuf.ByteString import pravda.node.servers.Abci.StoredProgram import utest._ -object BsonTranscode extends TestSuite { +import pravda.node.data.serialization.json._ + +object BJsonTranscode extends TestSuite { val tests = Tests { "trancode from case class" - { import pravda.node.data.serialization._ - import pravda.node.data.serialization.bson._ + import pravda.node.data.serialization.bjson._ val storedProrgram = StoredProgram(ByteString.copyFrom(Array[Byte](0x01, 0x02)), `sealed` = false) - val bson = transcode(storedProrgram).to[Bson] - transcode(Bson @@ bson).to[StoredProgram] ==> storedProrgram + val bjson = transcode(storedProrgram).to[BJson] + transcode(bjson).to[StoredProgram] ==> storedProrgram } } } diff --git a/node/src/test/scala/pravda/node/TransactionResultJsonSpecification.scala b/node/src/test/scala/pravda/node/TransactionResultJsonSpecification.scala index c5383552..88dc87f2 100644 --- a/node/src/test/scala/pravda/node/TransactionResultJsonSpecification.scala +++ b/node/src/test/scala/pravda/node/TransactionResultJsonSpecification.scala @@ -5,6 +5,7 @@ import org.scalacheck.Prop.forAll import org.scalacheck.{Gen, Properties} import pravda.common.domain import pravda.common.domain.{Address, NativeCoin} +import pravda.node.data.common.TransactionId import pravda.node.data.serialization._ import pravda.node.data.serialization.json._ import pravda.node.servers.Abci.TransactionResult @@ -113,9 +114,15 @@ object TransactionResultJsonSpecification extends Properties("TransactionResultJ val executionResult: Gen[ExecutionResult] = Gen.oneOf(runtimeException.map(Left(_)), finalState.map(Right(_))) + val transactionId: Gen[TransactionId] = + byteString.map(TransactionId @@ _) + val transactionResult: Gen[TransactionResult] = - for (er <- executionResult; es <- Gen.listOf(effect)) - yield TransactionResult(er, es) + for { + id <- transactionId + er <- executionResult + es <- Gen.listOf(effect) + } yield TransactionResult(id, er, es) property("TransactionResult/write->read") = forAll(transactionResult) { txr => val json = transcode(txr).to[Json] diff --git a/plaintest/src/main/scala/pravda/plaintest/Plaintest.scala b/plaintest/src/main/scala/pravda/plaintest/Plaintest.scala index 554e7a9a..c66c2289 100644 --- a/plaintest/src/main/scala/pravda/plaintest/Plaintest.scala +++ b/plaintest/src/main/scala/pravda/plaintest/Plaintest.scala @@ -83,11 +83,10 @@ abstract class Plaintest[Input: Manifest, Output: Manifest] extends TestSuite { Predef.assert( res == output, s""" - |Expected: - |${yaml4s.renderYaml(Extraction.decompose(res)(formats))} - |Actual output: - |${yaml4s.renderYaml(Extraction.decompose(output)(formats))} - """.stripMargin + |Produced: + |${yaml4s.renderYamlDiff(Extraction.decompose(output)(formats), + Extraction.decompose(res)(formats))} + """.stripMargin ) case Left(err) => Predef.assert(false, s"${f.getName}: $err") } diff --git a/project/Dependencies.scala b/project/Dependencies.scala new file mode 100644 index 00000000..c5da454e --- /dev/null +++ b/project/Dependencies.scala @@ -0,0 +1,33 @@ +import sbt._ + +object Dependencies { + lazy val akkaHttp = "com.typesafe.akka" %% "akka-http" % "10.1.5" + lazy val akkaActor = "com.typesafe.akka" %% "akka-actor" % "2.5.12" + lazy val akkaStream = "com.typesafe.akka" %% "akka-stream" % "2.5.12" + lazy val scalaCheck = "org.scalacheck" %% "scalacheck" % "1.14.0" + lazy val catsCore = "org.typelevel" %% "cats-core" % "1.0.1" + lazy val superTagged = "org.rudogma" %% "supertagged" % "1.4" + lazy val uTest = "com.lihaoyi" %% "utest" % "0.6.3" + lazy val tethys = "com.tethys-json" %% "tethys" % "0.7.0.2" + lazy val tethysDerivation = "com.tethys-json" %% "tethys-derivation" % "0.7.0.2" + lazy val tethysJson4s = "com.tethys-json" %% "tethys-json4s" % "0.7.0.2" + lazy val json4sAst = "org.json4s" %% "json4s-ast" % "3.6.1" + lazy val json4sNative = "org.json4s" %% "json4s-native" % "3.6.1" + lazy val fastParse = "com.lihaoyi" %% "fastparse" % "1.0.0" + lazy val fastParseByte = "com.lihaoyi" %% "fastparse-byte" % "1.0.0" + lazy val protobufJava = "com.google.protobuf" % "protobuf-java" % "3.5.0" + lazy val pprint = "com.lihaoyi" %% "pprint" % "0.5.3" + lazy val commonsIo = "commons-io" % "commons-io" % "2.6" + lazy val snakeYml = "org.yaml" % "snakeyaml" % "1.23" + lazy val scopt = "com.github.scopt" %% "scopt" % "3.7.0" + lazy val javaCompiler = "com.github.spullara.mustache.java" % "compiler" % "0.9.5" + lazy val contextual = "com.propensive" %% "contextual" % "1.1.0" + lazy val curve25519Java = "org.whispersystems" % "curve25519-java" % "0.4.1" + lazy val levelDb = "org.iq80.leveldb" % "leveldb" % "0.10" + lazy val korolevServerAkkaHttp = "com.github.fomkin" %% "korolev-server-akkahttp" % "0.7.0" + lazy val pureConfig = "com.github.pureconfig" %% "pureconfig" % "0.9.1" + lazy val shapeless = "com.chuusai" %% "shapeless" % "2.3.3" + lazy val akkaStreamUnixDomainSocket = "com.lightbend.akka" %% "akka-stream-alpakka-unix-domain-socket" % "0.17" + + lazy val exploadAbciServer = "com.expload" %% "scala-abci-server" % "0.13.0" +} diff --git a/services/broadcaster/src/main/resources/application.conf b/services/broadcaster/src/main/resources/application.conf new file mode 100644 index 00000000..366af06f --- /dev/null +++ b/services/broadcaster/src/main/resources/application.conf @@ -0,0 +1,6 @@ +http { + host = "0.0.0.0" + host = ${?HTTP_HOST} + port = 5000 + port = ${?HTTP_PORT} +} diff --git a/services/broadcaster/src/main/scala/pravda/broadcaster/ApiRoute.scala b/services/broadcaster/src/main/scala/pravda/broadcaster/ApiRoute.scala new file mode 100644 index 00000000..ee1deb30 --- /dev/null +++ b/services/broadcaster/src/main/scala/pravda/broadcaster/ApiRoute.scala @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2018 Expload.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package pravda.broadcaster + +import java.util.Base64 + +import akka.http.scaladsl.model._ +import akka.http.scaladsl.server.Directives._ +import akka.http.scaladsl.server.Route +import akka.http.scaladsl.unmarshalling.Unmarshaller +import com.google.protobuf.ByteString +import pravda.common.bytes +import pravda.common.domain.{Address, NativeCoin} +import pravda.node.client.impl.NodeLanguageImpl +import pravda.node.data.blockchain.TransactionData +import pravda.node.data.cryptography._ +import pravda.node.data.serialization.json._ +import pravda.node.utils.AkkaHttpSpecials._ +import pravda.vm.asm.PravdaAssembler + +import scala.concurrent.duration._ + +class ApiRoute(api: NodeLanguageImpl, url: String, publicKey: Address, secretKey: PrivateKey) { + + implicit val hexUnmarshaller: Unmarshaller[String, ByteString] = + Unmarshaller.strict(hex => bytes.hex2byteString(hex)) + + val route: Route = + post { + withoutRequestTimeout { + path("broadcast") { + parameters(('wattLimit.as[Long], 'wattPrice.as[Long])) { (wattLimit, wattPrice) => + extractStrictEntity(1.second) { body => + bodyToTransactionData(body).fold( + errorMessage => complete((StatusCodes.BadRequest, errorMessage)), + program => { + val future = api.singAndBroadcastTransaction( + url, + publicKey, + secretKey, + None, + wattLimit, + NativeCoin @@ wattPrice, + None, + program + ) + + onSuccess(future) { + case Left(s) => complete((StatusCodes.BadRequest, s)) + case Right(s) => complete(s) + } + } + ) + } + } + } + } + } + + def bodyToTransactionData(body: HttpEntity.Strict): Either[String, ByteString] = { + body.contentType.mediaType match { + case MediaTypes.`application/octet-stream` => + val data = TransactionData @@ ByteString.copyFrom(body.data.toArray) + Right(data) + case MediaTypes.`application/base64` => + val bytes = Base64.getDecoder.decode(body.data.toArray) + val data = TransactionData @@ ByteString.copyFrom(bytes) + Right(data) + case MediaTypes.`text/x-asm` | MediaTypes.`text/plain` => + PravdaAssembler.assemble(body.data.utf8String, true).left.map(_.mkString) + case mediaType => + Left("Bad media type:" + mediaType) + } + } +} diff --git a/services/broadcaster/src/main/scala/pravda/broadcaster/Application.scala b/services/broadcaster/src/main/scala/pravda/broadcaster/Application.scala new file mode 100644 index 00000000..c50ae893 --- /dev/null +++ b/services/broadcaster/src/main/scala/pravda/broadcaster/Application.scala @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2018 Expload.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package pravda.broadcaster +import akka.actor.ActorSystem +import akka.http.scaladsl.Http +import akka.stream.ActorMaterializer +import pravda.common.domain.Address +import pravda.node.client.impl.NodeLanguageImpl +import pravda.node.data.cryptography.PrivateKey + +import scala.concurrent.ExecutionContextExecutor +import scala.util.Success + +object Application extends App { + + implicit val system: ActorSystem = ActorSystem("pravda-node-client-api") + implicit val materializer: ActorMaterializer = ActorMaterializer() + implicit val executionContext: ExecutionContextExecutor = system.dispatcher + + final case class HttpConfig(host: String, port: Int) + val config = pureconfig.loadConfigOrThrow[HttpConfig]("http") + + val broadcastEndpoint = sys.env("PRAVDA_BROADCAST_ENDPOINT") + val broadcastPk = Address.fromHex(sys.env("PRAVDA_BROADCAST_PK")) + val broadcastSk = PrivateKey.fromHex(sys.env("PRAVDA_BROADCAST_SK")) + + lazy val nodeLanguage = new NodeLanguageImpl() + lazy val apiRoute = new ApiRoute(nodeLanguage, broadcastEndpoint, broadcastPk, broadcastSk) + + Http().bindAndHandle(apiRoute.route, config.host, config.port) andThen { + case Success(_) => println(s"API server started at ${config.host}:${config.port}") + } +} diff --git a/testkit/src/test/resources/Objects/Inheritance.sbox b/testkit/src/test/resources/Objects/Inheritance.sbox new file mode 100644 index 00000000..97130b47 --- /dev/null +++ b/testkit/src/test/resources/Objects/Inheritance.sbox @@ -0,0 +1,35 @@ +vm: + stack: + [utf8.TestInheritance] + storage: + utf8.init: "null" +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: Inheritance.exe + sources: + - Pravda.dll + - dotnet-tests/resources/Inheritance.cs + optimize: true +--- +stack: + - int32.387 +heap: + ref.0: + utf8.AVal: int32.100 + utf8.Answer: offset.1231 + utf8.AnswerPlus1: offset.2285 + ref.1: + utf8.BVal: int32.200 + utf8.Answer: offset.1684 + utf8.AnswerPlus1: offset.2285 +storage: + utf8.init: "null" +effects: + - eventType: StorageRead + program: "0000000000000000000000000000000000000000000000000000000000000000" + key: utf8.init + value: "null" diff --git a/testkit/src/test/resources/objects/buffer.sbox b/testkit/src/test/resources/Objects/IntBuffer.sbox similarity index 63% rename from testkit/src/test/resources/objects/buffer.sbox rename to testkit/src/test/resources/Objects/IntBuffer.sbox index 7973186a..45f9aad9 100644 --- a/testkit/src/test/resources/objects/buffer.sbox +++ b/testkit/src/test/resources/Objects/IntBuffer.sbox @@ -1,9 +1,19 @@ -stack: - [utf8.Func] -storage: - utf8.init: "null" -dotnetCompilation: |- - buffer.cs +vm: + stack: + [utf8.TestBuffer] + storage: + utf8.init: "null" +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: IntBuffer.exe + sources: + - Pravda.dll + - dotnet-tests/resources/IntBuffer.cs + optimize: true --- stack: - utf8.13510 diff --git a/testkit/src/test/resources/objects/inheritance.sbox b/testkit/src/test/resources/Objects/Object.sbox similarity index 50% rename from testkit/src/test/resources/objects/inheritance.sbox rename to testkit/src/test/resources/Objects/Object.sbox index aa620f91..8f752763 100644 --- a/testkit/src/test/resources/objects/inheritance.sbox +++ b/testkit/src/test/resources/Objects/Object.sbox @@ -1,23 +1,29 @@ -stack: - [utf8.Func] -storage: - utf8.init: "null" -dotnetCompilation: |- - inheritance.cs +vm: + stack: + [utf8.TestObjects] + storage: + utf8.init: "null" +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: Object.exe + sources: + - Pravda.dll + - dotnet-tests/resources/Object.cs + optimize: true --- stack: - - int32.387 + - int32.385 heap: ref.0: utf8.AVal: int32.100 - utf8.Answer: offset.1184 - utf8.AnswerPlus1: offset.2729 ref.1: utf8.BVal: int32.200 - utf8.Answer: offset.1817 - utf8.AnswerPlus1: offset.2729 storage: - utf8.init: "null" + utf8.init: "null" effects: - eventType: StorageRead program: "0000000000000000000000000000000000000000000000000000000000000000" diff --git a/testkit/src/test/resources/Objects/ObjectGetSet.sbox b/testkit/src/test/resources/Objects/ObjectGetSet.sbox new file mode 100644 index 00000000..fd33cb66 --- /dev/null +++ b/testkit/src/test/resources/Objects/ObjectGetSet.sbox @@ -0,0 +1,29 @@ +vm: + stack: + [utf8.TestObjectGetSet] + storage: + utf8.init: "null" +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: ObjectGetSet.exe + sources: + - Pravda.dll + - dotnet-tests/resources/ObjectGetSet.cs + optimize: true +--- +stack: + - int32.123 +heap: + ref.0: + utf8.field3: int32.100 + utf8.k__BackingField: int32.20 + utf8.field1: int32.3 +effects: + - eventType: StorageRead + program: '0000000000000000000000000000000000000000000000000000000000000000' + key: utf8.init + value: 'null' diff --git a/testkit/src/test/resources/Objects/ObjectInit.sbox b/testkit/src/test/resources/Objects/ObjectInit.sbox new file mode 100644 index 00000000..89d19709 --- /dev/null +++ b/testkit/src/test/resources/Objects/ObjectInit.sbox @@ -0,0 +1,30 @@ +vm: + stack: + [utf8.TestObjectInit] + storage: + utf8.init: "null" +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: ObjectInit.exe + sources: + - Pravda.dll + - dotnet-tests/resources/ObjectInit.cs + optimize: true +--- +heap: + ref.0: + utf8.bs: bytes. + utf8.d: number.0.0 + utf8.str: utf8. + utf8.s: int16.0 + utf8.i: int32.0 + utf8.b: int16.0 +effects: + - eventType: StorageRead + program: '0000000000000000000000000000000000000000000000000000000000000000' + key: utf8.init + value: 'null' diff --git a/testkit/src/test/resources/SmartProgram.generated.cs b/testkit/src/test/resources/SmartProgram.generated.cs new file mode 100644 index 00000000..d6fa9987 --- /dev/null +++ b/testkit/src/test/resources/SmartProgram.generated.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections; +using Expload.Unity.Codegen; + +namespace Expload.Pravda.SmartProgram +{ + public class BalanceOfRequest: ProgramRequest + { + public BalanceOfRequest(byte[] programAddress) : base(programAddress) { } + + protected override int ParseResult(string elem) + { + return ExploadTypeConverters.ParseInt32(elem); + } + + public IEnumerator Test(byte[] arg0) + { + yield return SendRequest("BalanceOf", new string[] { ExploadTypeConverters.PrintBytes(arg0) }, true); + } + + public IEnumerator Call(byte[] arg0) + { + yield return SendRequest("BalanceOf", new string[] { ExploadTypeConverters.PrintBytes(arg0) }, false); + } + + // Same as Call + // Deprecated + public IEnumerator BalanceOf(byte[] arg0) + { + yield return SendRequest("BalanceOf", new string[] { ExploadTypeConverters.PrintBytes(arg0) }, false); + } + } + public class EmitRequest: ProgramRequest + { + public EmitRequest(byte[] programAddress) : base(programAddress) { } + + protected override object ParseResult(string elem) + { + return ExploadTypeConverters.ParseNull(elem); + } + + public IEnumerator Test(byte[] arg0, int arg1) + { + yield return SendRequest("Emit", new string[] { ExploadTypeConverters.PrintBytes(arg0), ExploadTypeConverters.PrintInt32(arg1) }, true); + } + + public IEnumerator Call(byte[] arg0, int arg1) + { + yield return SendRequest("Emit", new string[] { ExploadTypeConverters.PrintBytes(arg0), ExploadTypeConverters.PrintInt32(arg1) }, false); + } + + // Same as Call + // Deprecated + public IEnumerator Emit(byte[] arg0, int arg1) + { + yield return SendRequest("Emit", new string[] { ExploadTypeConverters.PrintBytes(arg0), ExploadTypeConverters.PrintInt32(arg1) }, false); + } + } + public class TransferRequest: ProgramRequest + { + public TransferRequest(byte[] programAddress) : base(programAddress) { } + + protected override object ParseResult(string elem) + { + return ExploadTypeConverters.ParseNull(elem); + } + + public IEnumerator Test(byte[] arg0, int arg1) + { + yield return SendRequest("Transfer", new string[] { ExploadTypeConverters.PrintBytes(arg0), ExploadTypeConverters.PrintInt32(arg1) }, true); + } + + public IEnumerator Call(byte[] arg0, int arg1) + { + yield return SendRequest("Transfer", new string[] { ExploadTypeConverters.PrintBytes(arg0), ExploadTypeConverters.PrintInt32(arg1) }, false); + } + + // Same as Call + // Deprecated + public IEnumerator Transfer(byte[] arg0, int arg1) + { + yield return SendRequest("Transfer", new string[] { ExploadTypeConverters.PrintBytes(arg0), ExploadTypeConverters.PrintInt32(arg1) }, false); + } + } +} \ No newline at end of file diff --git a/testkit/src/test/resources/smart_program/balanceOf.sbox b/testkit/src/test/resources/SmartProgram/BalanceOf.sbox similarity index 53% rename from testkit/src/test/resources/smart_program/balanceOf.sbox rename to testkit/src/test/resources/SmartProgram/BalanceOf.sbox index 3a250526..cd9fc5e5 100644 --- a/testkit/src/test/resources/smart_program/balanceOf.sbox +++ b/testkit/src/test/resources/SmartProgram/BalanceOf.sbox @@ -1,10 +1,20 @@ -stack: - [bytes.0011, utf8.balanceOf] -storage: - utf8.init: "null" - bytes.62616C616E6365730011: int32.10 -dotnetCompilation: |- - smart_program.cs +vm: + stack: + [bytes.0011, utf8.BalanceOf] + storage: + utf8.init: "null" + bytes.42616C616E6365730011: int32.10 +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: SmartProgram.exe + sources: + - Pravda.dll + - dotnet-tests/resources/SmartProgram.cs + optimize: true --- stack: [int32.10] @@ -18,9 +28,9 @@ effects: value: "null" - eventType: StorageRead program: "0000000000000000000000000000000000000000000000000000000000000000" - key: bytes.62616c616e6365730011 + key: bytes.42616c616e6365730011 value: int32.10 - eventType: StorageRead program: "0000000000000000000000000000000000000000000000000000000000000000" - key: bytes.62616c616e6365730011 + key: bytes.42616c616e6365730011 value: int32.10 diff --git a/testkit/src/test/resources/SmartProgram/BalanceOfNoAddress.sbox b/testkit/src/test/resources/SmartProgram/BalanceOfNoAddress.sbox new file mode 100644 index 00000000..2aabecfd --- /dev/null +++ b/testkit/src/test/resources/SmartProgram/BalanceOfNoAddress.sbox @@ -0,0 +1,29 @@ +vm: + stack: + [bytes.0011, utf8.BalanceOf] + storage: + utf8.init: "null" +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: SmartProgram.exe + sources: + - Pravda.dll + - dotnet-tests/resources/SmartProgram.cs + optimize: true +--- +stack: + [int32.0] +storage: + utf8.init: "null" +effects: + - eventType: StorageRead + program: "0000000000000000000000000000000000000000000000000000000000000000" + key: utf8.init + value: "null" + - eventType: StorageRead + program: "0000000000000000000000000000000000000000000000000000000000000000" + key: bytes.42616c616e6365730011 \ No newline at end of file diff --git a/testkit/src/test/resources/SmartProgram/BalanceOfNoCtor.sbox b/testkit/src/test/resources/SmartProgram/BalanceOfNoCtor.sbox new file mode 100644 index 00000000..7af0fe90 --- /dev/null +++ b/testkit/src/test/resources/SmartProgram/BalanceOfNoCtor.sbox @@ -0,0 +1,27 @@ +vm: + stack: + [bytes.0011, utf8.BalanceOf] + storage: + bytes.42616C616E6365730011: int32.10 +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: SmartProgram.exe + sources: + - Pravda.dll + - dotnet-tests/resources/SmartProgram.cs + optimize: true +--- +stack: + - bytes.0011 + - utf8.BalanceOf +effects: + - eventType: StorageRead + program: "0000000000000000000000000000000000000000000000000000000000000000" + key: utf8.init +error: + code: 700 + message: Program was not initialized diff --git a/testkit/src/test/resources/SmartProgram/BalanceOfWithError.sbox b/testkit/src/test/resources/SmartProgram/BalanceOfWithError.sbox new file mode 100644 index 00000000..41d04d0a --- /dev/null +++ b/testkit/src/test/resources/SmartProgram/BalanceOfWithError.sbox @@ -0,0 +1,35 @@ +vm: + stack: + ["null", utf8.BalanceOf] + storage: + utf8.init: "null" + bytes.42616C616E6365730011: int32.10 +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: SmartProgram.exe + sources: + - Pravda.dll + - dotnet-tests/resources/SmartProgram.cs + optimize: true +--- +stack: + - "null" + - utf8.BalanceOf + - bytes.42616c616e636573 + - "null" + - int32.0 +storage: + bytes.42616c616e6365730011: int32.10 + utf8.init: "null" +effects: + - eventType: StorageRead + program: "0000000000000000000000000000000000000000000000000000000000000000" + key: utf8.init + value: "null" +error: + code: 701 + message: "Unexpected type: Null" \ No newline at end of file diff --git a/testkit/src/test/resources/smart_program/transfer.sbox b/testkit/src/test/resources/SmartProgram/Transfer.sbox similarity index 58% rename from testkit/src/test/resources/smart_program/transfer.sbox rename to testkit/src/test/resources/SmartProgram/Transfer.sbox index 5100173e..b545f4aa 100644 --- a/testkit/src/test/resources/smart_program/transfer.sbox +++ b/testkit/src/test/resources/SmartProgram/Transfer.sbox @@ -1,14 +1,24 @@ -stack: - [bytes.0011, int32.50, utf8.transfer] -storage: - utf8.init: "null" - bytes.62616C616E6365730102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20: int32.100 -dotnetCompilation: |- - smart_program.cs +vm: + stack: + [bytes.0011, int32.50, utf8.Transfer] + storage: + utf8.init: "null" + bytes.42616C616E6365730102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20: int32.100 +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: SmartProgram.exe + sources: + - Pravda.dll + - dotnet-tests/resources/SmartProgram.cs + optimize: true --- storage: - bytes.62616c616e6365730011: int32.50 - bytes.62616c616e6365730102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20: int32.50 + bytes.42616c616e6365730011: int32.50 + bytes.42616c616e6365730102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20: int32.50 utf8.init: "null" effects: - eventType: StorageRead @@ -17,29 +27,29 @@ effects: value: "null" - eventType: StorageRead program: "0000000000000000000000000000000000000000000000000000000000000000" - key: bytes.62616c616e6365730102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 + key: bytes.42616c616e6365730102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 value: int32.100 - eventType: StorageRead program: "0000000000000000000000000000000000000000000000000000000000000000" - key: bytes.62616c616e6365730102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 + key: bytes.42616c616e6365730102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 value: int32.100 - eventType: StorageRead program: "0000000000000000000000000000000000000000000000000000000000000000" - key: bytes.62616c616e6365730102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 + key: bytes.42616c616e6365730102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 value: int32.100 - eventType: StorageRead program: "0000000000000000000000000000000000000000000000000000000000000000" - key: bytes.62616c616e6365730102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 + key: bytes.42616c616e6365730102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 value: int32.100 - eventType: StorageWrite program: "0000000000000000000000000000000000000000000000000000000000000000" previous: int32.100 - key: bytes.62616c616e6365730102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 + key: bytes.42616c616e6365730102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 value: int32.50 - eventType: StorageRead program: "0000000000000000000000000000000000000000000000000000000000000000" - key: bytes.62616c616e6365730011 + key: bytes.42616c616e6365730011 - eventType: StorageWrite program: "0000000000000000000000000000000000000000000000000000000000000000" - key: bytes.62616c616e6365730011 + key: bytes.42616c616e6365730011 value: int32.50 diff --git a/testkit/src/test/resources/SmartProgram/WrongMethod.sbox b/testkit/src/test/resources/SmartProgram/WrongMethod.sbox new file mode 100644 index 00000000..9c516e9b --- /dev/null +++ b/testkit/src/test/resources/SmartProgram/WrongMethod.sbox @@ -0,0 +1,29 @@ +vm: + stack: + [utf8.Boom] + storage: + utf8.init: "null" +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: SmartProgram.exe + sources: + - Pravda.dll + - dotnet-tests/resources/SmartProgram.cs + optimize: true +--- +stack: + [utf8.Boom] +storage: + utf8.init: "null" +effects: + - eventType: StorageRead + program: "0000000000000000000000000000000000000000000000000000000000000000" + key: utf8.init + value: "null" +error: + code: 700 + message: Wrong method name diff --git a/testkit/src/test/resources/Stdlib/ScallEd25519Verify.sbox b/testkit/src/test/resources/Stdlib/ScallEd25519Verify.sbox new file mode 100644 index 00000000..f1266075 --- /dev/null +++ b/testkit/src/test/resources/Stdlib/ScallEd25519Verify.sbox @@ -0,0 +1,29 @@ +vm: + stack: + - bytes.5877667B812E22A18CF4555D0E5982E5584606214C26B47C967A19C9720E67E3 + - utf8.hello world + - bytes.C9C4F938E388EA5D4F19B939ABD4B0B2DDA3D8F447A34E1B782E36BDC8C65E6368E712166907396C37F6C0B6E28E6F61AD36933142D0B1AB69E63EC9F4300104 + - utf8.ValidateEd25519Signature + storage: + utf8.init: "null" +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: Stdlib.exe + sources: + - Pravda.dll + - dotnet-tests/resources/Stdlib.cs + optimize: true +--- +stack: + - bool.true +storage: + utf8.init: "null" +effects: + - eventType: StorageRead + program: "0000000000000000000000000000000000000000000000000000000000000000" + key: utf8.init + value: "null" \ No newline at end of file diff --git a/testkit/src/test/resources/Stdlib/ScallEd25519VerifyFalse.sbox b/testkit/src/test/resources/Stdlib/ScallEd25519VerifyFalse.sbox new file mode 100644 index 00000000..214612a0 --- /dev/null +++ b/testkit/src/test/resources/Stdlib/ScallEd25519VerifyFalse.sbox @@ -0,0 +1,29 @@ +vm: + stack: + - bytes.5877667B812E22A18CF4555D0E5982E5584606214C26B47C967A19C9720E67E3 + - utf8.hello world + - bytes.D1C4F938E388EA5D4F19B939ABD4B0B2DDA3D8F447A34E1B782E36BDC8C65E6368E712166907396C37F6C0B6E28E6F61AD36933142D0B1AB69E63EC9F4300104 + - utf8.ValidateEd25519Signature + storage: + utf8.init: "null" +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: Stdlib.exe + sources: + - Pravda.dll + - dotnet-tests/resources/Stdlib.cs + optimize: true +--- +stack: + - bool.false +storage: + utf8.init: "null" +effects: + - eventType: StorageRead + program: "0000000000000000000000000000000000000000000000000000000000000000" + key: utf8.init + value: "null" diff --git a/testkit/src/test/resources/Stdlib/ScallRipemd160.sbox b/testkit/src/test/resources/Stdlib/ScallRipemd160.sbox new file mode 100644 index 00000000..2991f8c8 --- /dev/null +++ b/testkit/src/test/resources/Stdlib/ScallRipemd160.sbox @@ -0,0 +1,27 @@ +vm: + stack: + - utf8.hello world + - utf8.Ripemd160 + storage: + utf8.init: "null" +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: Stdlib.exe + sources: + - Pravda.dll + - dotnet-tests/resources/Stdlib.cs + optimize: true +--- +stack: + - bytes.98c615784ccb5fe5936fbc0cbe9dfdb408d92f0f +storage: + utf8.init: "null" +effects: + - eventType: StorageRead + program: "0000000000000000000000000000000000000000000000000000000000000000" + key: utf8.init + value: "null" diff --git a/testkit/src/test/resources/Vm/BlockHash.sbox b/testkit/src/test/resources/Vm/BlockHash.sbox new file mode 100644 index 00000000..86980031 --- /dev/null +++ b/testkit/src/test/resources/Vm/BlockHash.sbox @@ -0,0 +1,29 @@ +vm: + stack: + [utf8.TestLastBlockHash] + storage: + utf8.init: "null" + app-state-info: + app-hash: 62099c6a16853f70fcf2e5a24da6e46faaf0b2541658bec668527b0436d32ece + height: 4 +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: Block.exe + sources: + - Pravda.dll + - dotnet-tests/resources/Block.cs + optimize: true +--- +stack: + - bytes.62099c6a16853f70fcf2e5a24da6e46faaf0b2541658bec668527b0436d32ece +storage: + utf8.init: "null" +effects: + - eventType: StorageRead + program: "0000000000000000000000000000000000000000000000000000000000000000" + key: utf8.init + value: "null" \ No newline at end of file diff --git a/testkit/src/test/resources/Vm/BlockHeight.sbox b/testkit/src/test/resources/Vm/BlockHeight.sbox new file mode 100644 index 00000000..e0430cca --- /dev/null +++ b/testkit/src/test/resources/Vm/BlockHeight.sbox @@ -0,0 +1,29 @@ +vm: + stack: + [utf8.TestHeightMethod] + storage: + utf8.init: "null" + app-state-info: + app-hash: 62099c6a16853f70fcf2e5a24da6e46faaf0b2541658bec668527b0436d32ece + height: 4 +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: Block.exe + sources: + - Pravda.dll + - dotnet-tests/resources/Block.cs + optimize: true +--- +stack: + - int64.4 +storage: + utf8.init: "null" +effects: + - eventType: StorageRead + program: "0000000000000000000000000000000000000000000000000000000000000000" + key: utf8.init + value: "null" \ No newline at end of file diff --git a/testkit/src/test/resources/Vm/Callers.sbox b/testkit/src/test/resources/Vm/Callers.sbox new file mode 100644 index 00000000..064be6a5 --- /dev/null +++ b/testkit/src/test/resources/Vm/Callers.sbox @@ -0,0 +1,28 @@ +vm: + stack: + [utf8.TestCallers] + storage: + utf8.init: "null" +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: Callers.exe + sources: + - Pravda.dll + - dotnet-tests/resources/Callers.cs + optimize: true +--- +stack: + - bytes.0000000000000000000000000000000000000000000000000000000000000000 +heap: + ref.0: [bytes, "0000000000000000000000000000000000000000000000000000000000000000"] +storage: + utf8.init: "null" +effects: + - eventType: StorageRead + program: '0000000000000000000000000000000000000000000000000000000000000000' + key: utf8.init + value: 'null' \ No newline at end of file diff --git a/testkit/src/test/resources/Vm/ConcatStrings.sbox b/testkit/src/test/resources/Vm/ConcatStrings.sbox new file mode 100644 index 00000000..00d426f8 --- /dev/null +++ b/testkit/src/test/resources/Vm/ConcatStrings.sbox @@ -0,0 +1,114 @@ +vm: + stack: + [utf8.TestConcatStrings] + storage: + utf8.init: "null" +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: ConcatStrings.exe + sources: + - Pravda.dll + - dotnet-tests/resources/ConcatStrings.cs + optimize: true +--- +stack: + - utf8.ss2sss3ssss4sssss5ssssss6sssssss7ssssssss8sssssssss9ssssssssss10 +heap: + ref.0: + - utf8 + - s + - s + - s + - s + - "4" + + ref.4: + - utf8 + - s + - s + - s + - s + - s + - s + - s + - s + - "8" + + ref.6: + - utf8 + - s + - s + - s + - s + - s + - s + - s + - s + - s + - s + - "10" + + ref.1: + - utf8 + - s + - s + - s + - s + - s + - "5" + + ref.3: + - utf8 + - s + - s + - s + - s + - s + - s + - s + - "7" + + ref.5: + - utf8 + - s + - s + - s + - s + - s + - s + - s + - s + - s + - "9" + + ref.2: + - utf8 + - s + - s + - s + - s + - s + - s + - "6" + + ref.7: + - utf8 + - ss2 + - sss3 + - ssss4 + - sssss5 + - ssssss6 + - sssssss7 + - ssssssss8 + - sssssssss9 + - ssssssssss10 +effects: + - eventType: StorageRead + program: "0000000000000000000000000000000000000000000000000000000000000000" + key: utf8.init + value: "null" + diff --git a/testkit/src/test/resources/vm/event.sbox b/testkit/src/test/resources/Vm/Event.sbox similarity index 75% rename from testkit/src/test/resources/vm/event.sbox rename to testkit/src/test/resources/Vm/Event.sbox index b0e5ab0f..f80092f9 100644 --- a/testkit/src/test/resources/vm/event.sbox +++ b/testkit/src/test/resources/Vm/Event.sbox @@ -1,9 +1,19 @@ -stack: - [utf8.MakeEvent] -storage: - utf8.init: "null" -dotnetCompilation: |- - event.cs +vm: + stack: + [utf8.TestEvent] + storage: + utf8.init: "null" +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: Event.exe + sources: + - Pravda.dll + - dotnet-tests/resources/Event.cs + optimize: true --- heap: ref.0: [int8, "1", "2", "3", "4"] diff --git a/testkit/src/test/resources/Vm/ExternalRegularMethods.sbox b/testkit/src/test/resources/Vm/ExternalRegularMethods.sbox new file mode 100644 index 00000000..082f100f --- /dev/null +++ b/testkit/src/test/resources/Vm/ExternalRegularMethods.sbox @@ -0,0 +1,41 @@ +vm: + stack: + [utf8.TestRegularMethods] + storage: + utf8.init: "null" + program-storage: + "123456789012345678901234567890123456789012345678901234567890ABCD": + utf8.init: "null" + programs: + "123456789012345678901234567890123456789012345678901234567890ABCD": + bytes.a1ff0b4343494ca1030b6450726f6772616d4e616d65737061636550726f6772616d5374617469634d6574686f647312110b4463746f7284a1010b4b6d6574686f645f63746f72110f014302110b44696e697453a1010b476d6574686f6473110f008e02110b5b50726f6772616d20776173206e6f7420696e697469616c697a6564a9a1000b476d6574686f647312110b4341646484a1010b4a6d6574686f645f416464110f00cc02110b5157726f6e67206d6574686f64206e616d65a9a1000b4a6d6574686f645f416464a1020d440b446e616d650b4341646403010103030001030b4972657475726e54706501031100110304131103041360110302151011030113a1010b474164645f6c7663110f011f01a1000b474164645f6c76631410141014101410a1010b4473746f70110f01e901a1000b4b6d6574686f645f63746f72a1020d420b446e616d650b4463746f720b4972657475726e5470650100110b44696e69745380a1010b4763746f725f6f6b110f01ac02110b6450726f6772616d20686173206265656e20616c726561647920696e697469616c697a6564a9a1000b4763746f725f6f6b1100110b44696e697450a1010b4863746f725f6c7663110f01d301a1000b4863746f725f6c766310a1010b4473746f70110f01e901a1000b4473746f70 +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: ExternalMethods.dll + sources: + - Pravda.dll + - dotnet-tests/resources/ExternalMethods.cs + optimize: true + - target: ExternalMethodsCheck.exe + sources: + - Pravda.dll + - ExternalMethods.dll + - dotnet-tests/resources/ExternalMethodsCheck.cs + optimize: true + main-class: ExternalNamespace.ExternalMethodsCheck +--- +stack: + - int32.2112 +heap: + ref.0: + utf8.A: int32.3 + utf8.B: int32.3 +effects: + - eventType: StorageRead + program: "0000000000000000000000000000000000000000000000000000000000000000" + key: utf8.init + value: "null" \ No newline at end of file diff --git a/testkit/src/test/resources/Vm/ExternalStaticMethods.sbox b/testkit/src/test/resources/Vm/ExternalStaticMethods.sbox new file mode 100644 index 00000000..6df10cf2 --- /dev/null +++ b/testkit/src/test/resources/Vm/ExternalStaticMethods.sbox @@ -0,0 +1,49 @@ +vm: + stack: + [utf8.TestStaticMethods] + storage: + utf8.init: "null" + program-storage: + "123456789012345678901234567890123456789012345678901234567890ABCD": + utf8.init: "null" + programs: + "123456789012345678901234567890123456789012345678901234567890ABCD": + bytes.a1ff0b4343494ca1030b6450726f6772616d4e616d65737061636550726f6772616d5374617469634d6574686f647312110b4463746f7284a1010b4b6d6574686f645f63746f72110f014302110b44696e697453a1010b476d6574686f6473110f008e02110b5b50726f6772616d20776173206e6f7420696e697469616c697a6564a9a1000b476d6574686f647312110b4341646484a1010b4a6d6574686f645f416464110f00cc02110b5157726f6e67206d6574686f64206e616d65a9a1000b4a6d6574686f645f416464a1020d440b446e616d650b4341646403010103030001030b4972657475726e54706501031100110304131103041360110302151011030113a1010b474164645f6c7663110f011f01a1000b474164645f6c76631410141014101410a1010b4473746f70110f01e901a1000b4b6d6574686f645f63746f72a1020d420b446e616d650b4463746f720b4972657475726e5470650100110b44696e69745380a1010b4763746f725f6f6b110f01ac02110b6450726f6772616d20686173206265656e20616c726561647920696e697469616c697a6564a9a1000b4763746f725f6f6b1100110b44696e697450a1010b4863746f725f6c7663110f01d301a1000b4863746f725f6c766310a1010b4473746f70110f01e901a1000b4473746f70 +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: ExternalMethods.dll + sources: + - Pravda.dll + - dotnet-tests/resources/ExternalMethods.cs + optimize: true + - target: ExternalMethodsCheck.exe + sources: + - Pravda.dll + - ExternalMethods.dll + - dotnet-tests/resources/ExternalMethodsCheck.cs + optimize: true + main-class: ExternalNamespace.ExternalMethodsCheck +--- +stack: + - int32.624 +effects: + - eventType: StorageRead + program: "0000000000000000000000000000000000000000000000000000000000000000" + key: utf8.init + value: "null" + - eventType: StorageRead + program: 123456789012345678901234567890123456789012345678901234567890abcd + key: utf8.init + value: "null" + - eventType: StorageRead + program: 123456789012345678901234567890123456789012345678901234567890abcd + key: utf8.init + value: "null" + - eventType: StorageRead + program: 123456789012345678901234567890123456789012345678901234567890abcd + key: utf8.init + value: "null" diff --git a/testkit/src/test/resources/Vm/LogicOperations.sbox b/testkit/src/test/resources/Vm/LogicOperations.sbox new file mode 100644 index 00000000..5020dbea --- /dev/null +++ b/testkit/src/test/resources/Vm/LogicOperations.sbox @@ -0,0 +1,46 @@ +vm: + stack: + [utf8.TestLogicOperations] + storage: + utf8.init: "null" +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: LogicOperations.exe + sources: + - Pravda.dll + - dotnet-tests/resources/LogicOperations.cs + optimize: true +--- +effects: + - eventType: StorageRead + program: '0000000000000000000000000000000000000000000000000000000000000000' + key: utf8.init + value: 'null' + - eventType: StorageWrite + program: '0000000000000000000000000000000000000000000000000000000000000000' + key: bytes.4c6f6701000000 + value: utf8.1 + - eventType: StorageWrite + program: '0000000000000000000000000000000000000000000000000000000000000000' + key: bytes.4c6f6702000000 + value: utf8.0 + - eventType: StorageWrite + program: '0000000000000000000000000000000000000000000000000000000000000000' + key: bytes.4c6f6703000000 + value: utf8.0 + - eventType: StorageWrite + program: '0000000000000000000000000000000000000000000000000000000000000000' + key: bytes.4c6f6704000000 + value: utf8.7 + - eventType: StorageWrite + program: '0000000000000000000000000000000000000000000000000000000000000000' + key: bytes.4c6f6705000000 + value: utf8.1 + - eventType: StorageWrite + program: '0000000000000000000000000000000000000000000000000000000000000000' + key: bytes.4c6f6706000000 + value: utf8.6 diff --git a/testkit/src/test/resources/Vm/Pcall.sbox b/testkit/src/test/resources/Vm/Pcall.sbox new file mode 100644 index 00000000..1bc580c3 --- /dev/null +++ b/testkit/src/test/resources/Vm/Pcall.sbox @@ -0,0 +1,79 @@ +vm: + stack: + [utf8.TestPcall] + storage: + utf8.init: "null" + program-storage: + "1eaed20b7ce2b336043e4b340b031f95bb1ce6d935ef733ae4df1b66e1e3d91f": + utf8.init: "null" + programs: + "1eaed20b7ce2b336043e4b340b031f95bb1ce6d935ef733ae4df1b66e1e3d91f": bytes.a1ff0b4343494c12110b4463746f7284a1010b4b6d6574686f645f63746f72110f011b02110b44696e697453a1010b476d6574686f6473110f006602110b5b50726f6772616d20776173206e6f7420696e697469616c697a6564a9a1000b476d6574686f647312110b4341646484a1010b4a6d6574686f645f416464110f00a402110b5157726f6e67206d6574686f64206e616d65a9a1000b4a6d6574686f645f416464a1020d440b446e616d650b4341646403010103030001030b4972657475726e54706501031100110304131103041360110302151011030113a1010b474164645f6c7663110f00f701a1000b474164645f6c76631410141014101410a1010b4473746f70110f01c101a1000b4b6d6574686f645f63746f72a1020d420b446e616d650b4463746f720b4972657475726e5470650100110b44696e69745380a1010b4763746f725f6f6b110f018402110b6450726f6772616d20686173206265656e20616c726561647920696e697469616c697a6564a9a1000b4763746f725f6f6b1100110b44696e697450a1010b4863746f725f6c7663110f01ab01a1000b4863746f725f6c766310a1010b4473746f70110f01c101a1000b4473746f70 +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: PcallProgram.dll + sources: + - Pravda.dll + - dotnet-tests/resources/PcallProgram.cs + optimize: true + - target: Pcall.exe + sources: + - Pravda.dll + - PcallProgram.dll + - dotnet-tests/resources/Pcall.cs + optimize: true + main-class: PcallNamespace.Pcall +--- +stack: + - int32.10 +heap: + ref.0: + - int8 + - '30' + - '-82' + - '-46' + - '11' + - '124' + - '-30' + - '-77' + - '54' + - '4' + - '62' + - '75' + - '52' + - '11' + - '3' + - '31' + - '-107' + - '-69' + - '28' + - '-26' + - '-39' + - '53' + - '-17' + - '115' + - '58' + - '-28' + - '-33' + - '27' + - '102' + - '-31' + - '-29' + - '-39' + - '31' +effects: + - eventType: StorageRead + program: '0000000000000000000000000000000000000000000000000000000000000000' + key: utf8.init + value: 'null' + - eventType: StorageRead + program: 1eaed20b7ce2b336043e4b340b031f95bb1ce6d935ef733ae4df1b66e1e3d91f + key: utf8.init + value: 'null' + - eventType: StorageRead + program: 1eaed20b7ce2b336043e4b340b031f95bb1ce6d935ef733ae4df1b66e1e3d91f + key: utf8.init + value: 'null' \ No newline at end of file diff --git a/testkit/src/test/resources/Vm/ProgramFields.sbox b/testkit/src/test/resources/Vm/ProgramFields.sbox new file mode 100644 index 00000000..07fba54e --- /dev/null +++ b/testkit/src/test/resources/Vm/ProgramFields.sbox @@ -0,0 +1,51 @@ +vm: + stack: + [utf8.ctor] +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: ProgramFields.exe + sources: + - Pravda.dll + - dotnet-tests/resources/ProgramFields.cs + optimize: true +--- +effects: + - eventType: StorageRead + program: '0000000000000000000000000000000000000000000000000000000000000000' + key: utf8.init + - eventType: StorageWrite + program: '0000000000000000000000000000000000000000000000000000000000000000' + key: utf8.init + value: 'null' + - eventType: StorageWrite + program: '0000000000000000000000000000000000000000000000000000000000000000' + key: utf8.p_Sbyte + value: int8.0 + - eventType: StorageWrite + program: '0000000000000000000000000000000000000000000000000000000000000000' + key: utf8.p_Short + value: int16.0 + - eventType: StorageWrite + program: '0000000000000000000000000000000000000000000000000000000000000000' + key: utf8.p_Int + value: int32.0 + - eventType: StorageWrite + program: '0000000000000000000000000000000000000000000000000000000000000000' + key: utf8.p_Double + value: number.0.0 + - eventType: StorageWrite + program: '0000000000000000000000000000000000000000000000000000000000000000' + key: utf8.p_String + value: utf8. + - eventType: StorageWrite + program: '0000000000000000000000000000000000000000000000000000000000000000' + key: utf8.p_Bytess + value: bytes. + - eventType: StorageWrite + program: '0000000000000000000000000000000000000000000000000000000000000000' + key: utf8.p_Object + value: 'null' \ No newline at end of file diff --git a/testkit/src/test/resources/Vm/ReturnObject.sbox b/testkit/src/test/resources/Vm/ReturnObject.sbox new file mode 100644 index 00000000..15dfc047 --- /dev/null +++ b/testkit/src/test/resources/Vm/ReturnObject.sbox @@ -0,0 +1,44 @@ +vm: + stack: + [utf8.TestReturnObject] + storage: + utf8.init: "null" + program-storage: + "123456789012345678901234567890123456789012345678901234567890ABCD": + utf8.init: "null" + programs: + "123456789012345678901234567890123456789012345678901234567890ABCD": + bytes.a1ff0b4343494ca1030b6152657475726e4f626a6563744e616d65737061636552657475726e4f626a65637412110b4463746f7284a1010b4b6d6574686f645f63746f72110f01cb02110b44696e697453a1010b476d6574686f6473110f008b02110b5b50726f6772616d20776173206e6f7420696e697469616c697a6564a9a1000b476d6574686f647312110b494765744f626a65637484a1010b506d6574686f645f4765744f626a656374110f00db02110b5157726f6e67206d6574686f64206e616d65a9a1000b506d6574686f645f4765744f626a65637411032a200d00a1010b67767461626c655f52657475726e4f626a6563744e616d6573706163652e536f6d654f626a656374110f039204a1010b6f64656661756c745f6669656c64735f52657475726e4f626a6563744e616d6573706163652e536f6d654f626a656374110f029c0411030215a1010b7066756e635f52657475726e4f626a6563744e616d6573706163652e536f6d654f626a6563742e63746f725f696e743332110f02e104a1010b4d4765744f626a6563745f6c7663110f01ad01a1000b4d4765744f626a6563745f6c76631410a1010b4473746f70110f039b01a1000b4b6d6574686f645f63746f72a1020d420b446e616d650b4463746f720b4972657475726e5470650100110b44696e69745380a1010b4763746f725f6f6b110f023402110b6450726f6772616d20686173206265656e20616c726561647920696e697469616c697a6564a9a1000b4763746f725f6f6b1100110b44696e697450a1010b4863746f725f6c7663110f025b01a1000b4863746f725f6c766310a1010b4473746f70110f039b01a1000b6f64656661756c745f6669656c64735f52657475726e4f626a6563744e616d6573706163652e536f6d654f626a65637412110300260b49536f6d654669656c6405a1000b7066756e635f52657475726e4f626a6563744e616d6573706163652e536f6d654f626a6563742e63746f725f696e74333211030213101103021311030213260b49536f6d654669656c64a1010b6f52657475726e4f626a6563744e616d6573706163652e536f6d654f626a6563742e63746f725f696e7433325f6c7663110f036501a1000b6f52657475726e4f626a6563744e616d6573706163652e536f6d654f626a6563742e63746f725f696e7433325f6c76631005a1000b67767461626c655f52657475726e4f626a6563744e616d6573706163652e536f6d654f626a65637405a1000b4473746f70 +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: ReturnObject.dll + sources: + - Pravda.dll + - dotnet-tests/resources/ReturnObject.cs + optimize: true + - target: ReturnObjectCheck.exe + sources: + - Pravda.dll + - ReturnObject.dll + - dotnet-tests/resources/ReturnObjectCheck.cs + optimize: true + main-class: ReturnObjectNamespace.ReturnObjectCheck +--- +stack: + - int32.42 +heap: + ref.0: + utf8.SomeField: int32.42 +effects: + - eventType: StorageRead + program: '0000000000000000000000000000000000000000000000000000000000000000' + key: utf8.init + value: 'null' + - eventType: StorageRead + program: 123456789012345678901234567890123456789012345678901234567890abcd + key: utf8.init + value: 'null' diff --git a/testkit/src/test/resources/Vm/StaticClass.sbox b/testkit/src/test/resources/Vm/StaticClass.sbox new file mode 100644 index 00000000..ed70841a --- /dev/null +++ b/testkit/src/test/resources/Vm/StaticClass.sbox @@ -0,0 +1,25 @@ +vm: + stack: + [bytes.0123456789ABCDEF, utf8.TestToHex] + storage: + utf8.init: "null" +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: StaticClass.exe + sources: + - Pravda.dll + - dotnet-tests/resources/StaticClass.cs + optimize: true +--- +stack: + - utf8.0123456789ABCDEF +effects: + - eventType: StorageRead + program: '0000000000000000000000000000000000000000000000000000000000000000' + key: utf8.init + value: 'null' + diff --git a/testkit/src/test/resources/Vm/SystemMethods.sbox b/testkit/src/test/resources/Vm/SystemMethods.sbox new file mode 100644 index 00000000..b88c008b --- /dev/null +++ b/testkit/src/test/resources/Vm/SystemMethods.sbox @@ -0,0 +1,34 @@ +vm: + stack: + [utf8.TestSystemMethods] + storage: + utf8.init: "null" +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: SystemMethods.exe + sources: + - Pravda.dll + - dotnet-tests/resources/SystemMethods.cs + optimize: true +--- +effects: + - eventType: StorageRead + program: '0000000000000000000000000000000000000000000000000000000000000000' + key: utf8.init + value: 'null' + - eventType: ShowBalance + address: '0000000000000000000000000000000000000000000000000000000000000000' + amount: 0 + - eventType: Transfer + from: 0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 + to: '0000000000000000000000000000000000000000000000000000000000000000' + amount: 100 + - eventType: Transfer + from: '0000000000000000000000000000000000000000000000000000000000000000' + to: '0000000000000000000000000000000000000000000000000000000000000000' + amount: 200 + diff --git a/testkit/src/test/resources/Vm/Throw.sbox b/testkit/src/test/resources/Vm/Throw.sbox new file mode 100644 index 00000000..c00e80f6 --- /dev/null +++ b/testkit/src/test/resources/Vm/Throw.sbox @@ -0,0 +1,27 @@ +vm: + stack: [utf8.TestThrow] + storage: + utf8.init: "null" +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + optimize: true + - target: VmOps.exe + sources: + - Pravda.dll + - dotnet-tests/resources/VmOps.cs + optimize: true +--- +stack: [utf8.TestThrow] +storage: + utf8.init: "null" +effects: + - eventType: StorageRead + program: "0000000000000000000000000000000000000000000000000000000000000000" + key: utf8.init + value: "null" +error: + code: 700 + message: Oops! \ No newline at end of file diff --git a/testkit/src/test/resources/ZooProgram.generated.cs b/testkit/src/test/resources/ZooProgram.generated.cs new file mode 100644 index 00000000..8fff4a47 --- /dev/null +++ b/testkit/src/test/resources/ZooProgram.generated.cs @@ -0,0 +1,137 @@ +using System; +using System.Collections; +using Expload.Unity.Codegen; + +namespace Expload.Pravda.ZooProgram +{ + public class BreedPetsRequest: ProgramRequest + { + public BreedPetsRequest(byte[] programAddress) : base(programAddress) { } + + protected override string ParseResult(string elem) + { + return ExploadTypeConverters.ParseUtf8(elem); + } + + public IEnumerator Test(string arg0, string arg1) + { + yield return SendRequest("BreedPets", new string[] { ExploadTypeConverters.PrintUtf8(arg0), ExploadTypeConverters.PrintUtf8(arg1) }, true); + } + + public IEnumerator Call(string arg0, string arg1) + { + yield return SendRequest("BreedPets", new string[] { ExploadTypeConverters.PrintUtf8(arg0), ExploadTypeConverters.PrintUtf8(arg1) }, false); + } + + // Same as Call + // Deprecated + public IEnumerator BreedPets(string arg0, string arg1) + { + yield return SendRequest("BreedPets", new string[] { ExploadTypeConverters.PrintUtf8(arg0), ExploadTypeConverters.PrintUtf8(arg1) }, false); + } + } + public class NewPetRequest: ProgramRequest + { + public NewPetRequest(byte[] programAddress) : base(programAddress) { } + + protected override string ParseResult(string elem) + { + return ExploadTypeConverters.ParseUtf8(elem); + } + + public IEnumerator Test(int arg0) + { + yield return SendRequest("NewPet", new string[] { ExploadTypeConverters.PrintInt32(arg0) }, true); + } + + public IEnumerator Call(int arg0) + { + yield return SendRequest("NewPet", new string[] { ExploadTypeConverters.PrintInt32(arg0) }, false); + } + + // Same as Call + // Deprecated + public IEnumerator NewPet(int arg0) + { + yield return SendRequest("NewPet", new string[] { ExploadTypeConverters.PrintInt32(arg0) }, false); + } + } + public class NewZooRequest: ProgramRequest + { + public NewZooRequest(byte[] programAddress) : base(programAddress) { } + + protected override int ParseResult(string elem) + { + return ExploadTypeConverters.ParseInt32(elem); + } + + public IEnumerator Test() + { + yield return SendRequest("NewZoo", new string[] { }, true); + } + + public IEnumerator Call() + { + yield return SendRequest("NewZoo", new string[] { }, false); + } + + // Same as Call + // Deprecated + public IEnumerator NewZoo() + { + yield return SendRequest("NewZoo", new string[] { }, false); + } + } + public class TransferPetRequest: ProgramRequest + { + public TransferPetRequest(byte[] programAddress) : base(programAddress) { } + + protected override object ParseResult(string elem) + { + return ExploadTypeConverters.ParseNull(elem); + } + + public IEnumerator Test(byte[] arg0, int arg1, string arg2) + { + yield return SendRequest("TransferPet", new string[] { ExploadTypeConverters.PrintBytes(arg0), ExploadTypeConverters.PrintInt32(arg1), ExploadTypeConverters.PrintUtf8(arg2) }, true); + } + + public IEnumerator Call(byte[] arg0, int arg1, string arg2) + { + yield return SendRequest("TransferPet", new string[] { ExploadTypeConverters.PrintBytes(arg0), ExploadTypeConverters.PrintInt32(arg1), ExploadTypeConverters.PrintUtf8(arg2) }, false); + } + + // Same as Call + // Deprecated + public IEnumerator TransferPet(byte[] arg0, int arg1, string arg2) + { + yield return SendRequest("TransferPet", new string[] { ExploadTypeConverters.PrintBytes(arg0), ExploadTypeConverters.PrintInt32(arg1), ExploadTypeConverters.PrintUtf8(arg2) }, false); + } + } + public class TransferZooRequest: ProgramRequest + { + public TransferZooRequest(byte[] programAddress) : base(programAddress) { } + + protected override object ParseResult(string elem) + { + return ExploadTypeConverters.ParseNull(elem); + } + + public IEnumerator Test(byte[] arg0, int arg1) + { + yield return SendRequest("TransferZoo", new string[] { ExploadTypeConverters.PrintBytes(arg0), ExploadTypeConverters.PrintInt32(arg1) }, true); + } + + public IEnumerator Call(byte[] arg0, int arg1) + { + yield return SendRequest("TransferZoo", new string[] { ExploadTypeConverters.PrintBytes(arg0), ExploadTypeConverters.PrintInt32(arg1) }, false); + } + + // Same as Call + // Deprecated + public IEnumerator TransferZoo(byte[] arg0, int arg1) + { + yield return SendRequest("TransferZoo", new string[] { ExploadTypeConverters.PrintBytes(arg0), ExploadTypeConverters.PrintInt32(arg1) }, false); + } + } +} \ No newline at end of file diff --git a/testkit/src/test/resources/zoo_program/breedPets.sbox b/testkit/src/test/resources/ZooProgram/BreedPets.sbox similarity index 78% rename from testkit/src/test/resources/zoo_program/breedPets.sbox rename to testkit/src/test/resources/ZooProgram/BreedPets.sbox index 240f41df..6bf483b1 100644 --- a/testkit/src/test/resources/zoo_program/breedPets.sbox +++ b/testkit/src/test/resources/ZooProgram/BreedPets.sbox @@ -1,16 +1,24 @@ -stack: - [utf8.pet1, utf8.pet2, utf8.BreedPets] -storage: - utf8.ZooCnt: int32.2 - utf8.PetId: int32.3 - bytes.5A6F6F546F4F776E657200000001: bytes.0102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20 - bytes.506574546F4F776E657270657431: bytes.0102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20 - bytes.506574546F4F776E657270657432: bytes.0102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20 - bytes.5065745369676E617475726570657431: bytes.1234 - bytes.5065745369676E617475726570657432: bytes.5678 - utf8.init: "null" -dotnetCompilation: |- - zoo_program.cs +vm: + stack: + [utf8.pet1, utf8.pet2, utf8.BreedPets] + storage: + utf8.ZooCnt: int32.2 + utf8.PetId: int32.3 + bytes.5A6F6F546F4F776E657200000001: bytes.0102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20 + bytes.506574546F4F776E657270657431: bytes.0102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20 + bytes.506574546F4F776E657270657432: bytes.0102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20 + bytes.5065745369676E617475726570657431: bytes.1234 + bytes.5065745369676E617475726570657432: bytes.5678 + utf8.init: "null" +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + - target: ZooProgram.exe + sources: + - Pravda.dll + - dotnet-tests/resources/ZooProgram.cs --- stack: [utf8.pet1pet2] diff --git a/testkit/src/test/resources/zoo_program/ctor.sbox b/testkit/src/test/resources/ZooProgram/Ctor.sbox similarity index 53% rename from testkit/src/test/resources/zoo_program/ctor.sbox rename to testkit/src/test/resources/ZooProgram/Ctor.sbox index a669cfcd..576daff4 100644 --- a/testkit/src/test/resources/zoo_program/ctor.sbox +++ b/testkit/src/test/resources/ZooProgram/Ctor.sbox @@ -1,7 +1,15 @@ -stack: - [utf8.ctor] -dotnetCompilation: |- - zoo_program.cs +vm: + stack: + [utf8.ctor] +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + - target: ZooProgram.exe + sources: + - Pravda.dll + - dotnet-tests/resources/ZooProgram.cs --- storage: utf8.p_ZooCnt: int32.1 @@ -18,8 +26,19 @@ effects: - eventType: StorageWrite program: "0000000000000000000000000000000000000000000000000000000000000000" key: utf8.p_ZooCnt - value: int32.1 + value: int32.0 - eventType: StorageWrite program: "0000000000000000000000000000000000000000000000000000000000000000" key: utf8.p_PetId + value: int32.0 + - program: '0000000000000000000000000000000000000000000000000000000000000000' + eventType: StorageWrite + previous: int32.0 + key: utf8.p_ZooCnt + value: int32.1 + - program: '0000000000000000000000000000000000000000000000000000000000000000' + eventType: StorageWrite + previous: int32.0 + key: utf8.p_PetId value: int32.1 + diff --git a/testkit/src/test/resources/zoo_program/newPet.sbox b/testkit/src/test/resources/ZooProgram/NewPet.sbox similarity index 79% rename from testkit/src/test/resources/zoo_program/newPet.sbox rename to testkit/src/test/resources/ZooProgram/NewPet.sbox index 02609239..f640e381 100644 --- a/testkit/src/test/resources/zoo_program/newPet.sbox +++ b/testkit/src/test/resources/ZooProgram/NewPet.sbox @@ -1,12 +1,20 @@ -stack: - [int32.1, utf8.NewPet] -storage: - utf8.p_ZooCnt: int32.2 - utf8.p_PetId: int32.1 - bytes.5A6F6F546F4F776E657200000001: bytes.0102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20 - utf8.init: "null" -dotnetCompilation: |- - zoo_program.cs +vm: + stack: + [int32.1, utf8.NewPet] + storage: + utf8.p_ZooCnt: int32.2 + utf8.p_PetId: int32.1 + bytes.5A6F6F546F4F776E657201000000: bytes.0102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20 + utf8.init: "null" +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + - target: ZooProgram.exe + sources: + - Pravda.dll + - dotnet-tests/resources/ZooProgram.cs --- stack: - utf8.pet1 @@ -37,11 +45,11 @@ effects: value: "null" - eventType: StorageRead program: "0000000000000000000000000000000000000000000000000000000000000000" - key: bytes.5a6f6f546f4f776e657200000001 + key: bytes.5a6f6f546f4f776e657201000000 value: bytes.0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 - eventType: StorageRead program: "0000000000000000000000000000000000000000000000000000000000000000" - key: bytes.5a6f6f546f4f776e657200000001 + key: bytes.5a6f6f546f4f776e657201000000 value: bytes.0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 - eventType: StorageRead program: "0000000000000000000000000000000000000000000000000000000000000000" diff --git a/testkit/src/test/resources/zoo_program/newZoo.sbox b/testkit/src/test/resources/ZooProgram/NewZoo.sbox similarity index 76% rename from testkit/src/test/resources/zoo_program/newZoo.sbox rename to testkit/src/test/resources/ZooProgram/NewZoo.sbox index 07395fa9..f3eb665a 100644 --- a/testkit/src/test/resources/zoo_program/newZoo.sbox +++ b/testkit/src/test/resources/ZooProgram/NewZoo.sbox @@ -1,11 +1,19 @@ -stack: - [utf8.NewZoo] -storage: - utf8.p_ZooCnt: int32.1 - utf8.p_PetId: int32.1 - utf8.init: "null" -dotnetCompilation: |- - zoo_program.cs +vm: + stack: + [utf8.NewZoo] + storage: + utf8.p_ZooCnt: int32.1 + utf8.p_PetId: int32.1 + utf8.init: "null" +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + - target: ZooProgram.exe + sources: + - Pravda.dll + - dotnet-tests/resources/ZooProgram.cs --- stack: - int32.1 @@ -25,7 +33,7 @@ effects: value: int32.1 - eventType: StorageWrite program: "0000000000000000000000000000000000000000000000000000000000000000" - key: bytes.5a6f6f546f4f776e657200000001 + key: bytes.5a6f6f546f4f776e657201000000 value: bytes.0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 - eventType: StorageRead program: "0000000000000000000000000000000000000000000000000000000000000000" diff --git a/testkit/src/test/resources/github/poker/Deal.sbox b/testkit/src/test/resources/github/Poker/Deal.sbox similarity index 75% rename from testkit/src/test/resources/github/poker/Deal.sbox rename to testkit/src/test/resources/github/Poker/Deal.sbox index 0c527361..934a1eeb 100644 --- a/testkit/src/test/resources/github/poker/Deal.sbox +++ b/testkit/src/test/resources/github/Poker/Deal.sbox @@ -1,10 +1,18 @@ -stack: - [bytes.01, bytes.02, bytes.03, bytes.04, bytes.05, bytes.06, bytes.07, bytes.08, bytes.09, utf8.Deal] -storage: - utf8.cardCombinations: bytes.1F2F4F3757673B5B6B733D5D6D75793E5E6E767A7C - utf8.init: "null" -dotnetCompilation: |- - poker.cs +vm: + stack: + [bytes.01, bytes.02, bytes.03, bytes.04, bytes.05, bytes.06, bytes.07, bytes.08, bytes.09, utf8.Deal] + storage: + utf8.cardCombinations: bytes.1F2F4F3757673B5B6B733D5D6D75793E5E6E767A7C + utf8.init: "null" +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + - target: Poker.exe + sources: + - Pravda.dll + - dotnet-tests/resources/Poker.cs --- heap: "ref.0": [int8, "8"] @@ -36,31 +44,31 @@ effects: value: bytes.01 - eventType: StorageWrite program: "0000000000000000000000000000000000000000000000000000000000000000" - key: bytes.506c617965727300000001 + key: bytes.506c617965727301000000 value: bytes.02 - eventType: StorageWrite program: "0000000000000000000000000000000000000000000000000000000000000000" - key: bytes.506c617965727300000002 + key: bytes.506c617965727302000000 value: bytes.03 - eventType: StorageWrite program: "0000000000000000000000000000000000000000000000000000000000000000" - key: bytes.506c617965727300000003 + key: bytes.506c617965727303000000 value: bytes.04 - eventType: StorageWrite program: "0000000000000000000000000000000000000000000000000000000000000000" - key: bytes.506c617965727300000004 + key: bytes.506c617965727304000000 value: bytes.05 - eventType: StorageWrite program: "0000000000000000000000000000000000000000000000000000000000000000" - key: bytes.506c617965727300000005 + key: bytes.506c617965727305000000 value: bytes.06 - eventType: StorageWrite program: "0000000000000000000000000000000000000000000000000000000000000000" - key: bytes.506c617965727300000006 + key: bytes.506c617965727306000000 value: bytes.07 - eventType: StorageWrite program: "0000000000000000000000000000000000000000000000000000000000000000" - key: bytes.506c617965727300000007 + key: bytes.506c617965727307000000 value: bytes.08 - eventType: StorageWrite program: "0000000000000000000000000000000000000000000000000000000000000000" diff --git a/testkit/src/test/resources/github/poker/DealPrivateCard.sbox b/testkit/src/test/resources/github/Poker/DealPrivateCard.sbox similarity index 71% rename from testkit/src/test/resources/github/poker/DealPrivateCard.sbox rename to testkit/src/test/resources/github/Poker/DealPrivateCard.sbox index e7d95630..a2209785 100644 --- a/testkit/src/test/resources/github/poker/DealPrivateCard.sbox +++ b/testkit/src/test/resources/github/Poker/DealPrivateCard.sbox @@ -1,11 +1,19 @@ -stack: - [int32.0, bytes.1234, utf8.DealPrivateCard] -storage: - bytes.506C617965727300000000: bytes.01 - utf8.cardCombinations: bytes.1F2F4F3757673B5B6B733D5D6D75793E5E6E767A7C - utf8.init: "null" -dotnetCompilation: |- - poker.cs +vm: + stack: + [int32.0, bytes.1234, utf8.DealPrivateCard] + storage: + bytes.506C617965727300000000: bytes.01 + utf8.cardCombinations: bytes.1F2F4F3757673B5B6B733D5D6D75793E5E6E767A7C + utf8.init: "null" +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + - target: Poker.exe + sources: + - Pravda.dll + - dotnet-tests/resources/Poker.cs --- storage: bytes.506c6179657243617264733101: bytes.1234 diff --git a/testkit/src/test/resources/github/poker/DealPublicCard.sbox b/testkit/src/test/resources/github/Poker/DealPublicCard.sbox similarity index 71% rename from testkit/src/test/resources/github/poker/DealPublicCard.sbox rename to testkit/src/test/resources/github/Poker/DealPublicCard.sbox index cdbf7bd9..ef92b37c 100644 --- a/testkit/src/test/resources/github/poker/DealPublicCard.sbox +++ b/testkit/src/test/resources/github/Poker/DealPublicCard.sbox @@ -1,10 +1,18 @@ -stack: - [int32.9, utf8.DealPublicCard] -storage: - utf8.cardCombinations: bytes.1F2F4F3757673B5B6B733D5D6D75793E5E6E767A7C - utf8.init: "null" -dotnetCompilation: |- - poker.cs +vm: + stack: + [int32.9, utf8.DealPublicCard] + storage: + utf8.cardCombinations: bytes.1F2F4F3757673B5B6B733D5D6D75793E5E6E767A7C + utf8.init: "null" +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + - target: Poker.exe + sources: + - Pravda.dll + - dotnet-tests/resources/Poker.cs --- storage: bytes.5461626c65436172647300000000: int32.9 diff --git a/testkit/src/test/resources/github/poker/DealWithError.sbox b/testkit/src/test/resources/github/Poker/DealWithError.sbox similarity index 65% rename from testkit/src/test/resources/github/poker/DealWithError.sbox rename to testkit/src/test/resources/github/Poker/DealWithError.sbox index 60126b16..3edc783a 100644 --- a/testkit/src/test/resources/github/poker/DealWithError.sbox +++ b/testkit/src/test/resources/github/Poker/DealWithError.sbox @@ -1,10 +1,18 @@ -stack: - [bytes.01, bytes.02, bytes.03, bytes.04, bytes.05, bytes.06, bytes.07, bytes.08, utf8.Deal] -storage: - utf8.cardCombinations: bytes.1F2F4F3757673B5B6B733D5D6D75793E5E6E767A7C - utf8.init: "null" -dotnetCompilation: |- - poker.cs +vm: + stack: + [bytes.01, bytes.02, bytes.03, bytes.04, bytes.05, bytes.06, bytes.07, bytes.08, utf8.Deal] + storage: + utf8.cardCombinations: bytes.1F2F4F3757673B5B6B733D5D6D75793E5E6E767A7C + utf8.init: "null" +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + - target: Poker.exe + sources: + - Pravda.dll + - dotnet-tests/resources/Poker.cs --- stack: - bytes.01 diff --git a/testkit/src/test/resources/github/Poker/GetCombinationValue1.sbox b/testkit/src/test/resources/github/Poker/GetCombinationValue1.sbox new file mode 100644 index 00000000..764b20c9 --- /dev/null +++ b/testkit/src/test/resources/github/Poker/GetCombinationValue1.sbox @@ -0,0 +1,26 @@ +vm: + stack: + [int32.0, int32.1, int32.2, int32.3, int32.4, utf8.GetCombinationValue] + storage: + utf8.cardCombinations: bytes.1F2F4F3757673B5B6B733D5D6D75793E5E6E767A7C + utf8.init: "null" +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + - target: Poker.exe + sources: + - Pravda.dll + - dotnet-tests/resources/Poker.cs +--- +stack: + - int32.1000000 +storage: + utf8.cardCombinations: bytes.1f2f4f3757673b5b6b733d5d6d75793e5e6e767a7c + utf8.init: "null" +effects: + - eventType: StorageRead + program: "0000000000000000000000000000000000000000000000000000000000000000" + key: utf8.init + value: "null" diff --git a/testkit/src/test/resources/github/Poker/GetCombinationValue2.sbox b/testkit/src/test/resources/github/Poker/GetCombinationValue2.sbox new file mode 100644 index 00000000..9d87afac --- /dev/null +++ b/testkit/src/test/resources/github/Poker/GetCombinationValue2.sbox @@ -0,0 +1,26 @@ +vm: + stack: + [int32.0, int32.2, int32.4, int32.6, int32.80, utf8.GetCombinationValue] + storage: + utf8.cardCombinations: bytes.1F2F4F3757673B5B6B733D5D6D75793E5E6E767A7C + utf8.init: "null" +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + - target: Poker.exe + sources: + - Pravda.dll + - dotnet-tests/resources/Poker.cs +--- +stack: + - int32.50 +storage: + utf8.cardCombinations: bytes.1f2f4f3757673b5b6b733d5d6d75793e5e6e767a7c + utf8.init: "null" +effects: + - eventType: StorageRead + program: "0000000000000000000000000000000000000000000000000000000000000000" + key: utf8.init + value: "null" diff --git a/testkit/src/test/resources/github/poker/GetHighestCombination.sbox b/testkit/src/test/resources/github/Poker/GetHighestCombination.sbox similarity index 94% rename from testkit/src/test/resources/github/poker/GetHighestCombination.sbox rename to testkit/src/test/resources/github/Poker/GetHighestCombination.sbox index 034715be..a1475902 100644 --- a/testkit/src/test/resources/github/poker/GetHighestCombination.sbox +++ b/testkit/src/test/resources/github/Poker/GetHighestCombination.sbox @@ -1,10 +1,18 @@ -stack: - [int32.13, int32.26, int32.1, int32.2, int32.16, int32.30, int32.31, utf8.GetHighestCombination] -storage: - utf8.p_cardCombinations: bytes.1F2F4F3757673B5B6B733D5D6D75793E5E6E767A7C - utf8.init: "null" -dotnetCompilation: |- - poker.cs +vm: + stack: + [int32.13, int32.26, int32.1, int32.2, int32.16, int32.30, int32.31, utf8.GetHighestCombination] + storage: + utf8.p_cardCombinations: bytes.1F2F4F3757673B5B6B733D5D6D75793E5E6E767A7C + utf8.init: "null" +dotnet-compilation: + steps: + - target: Pravda.dll + sources: + - PravdaDotNet/Pravda.cs + - target: Poker.exe + sources: + - Pravda.dll + - dotnet-tests/resources/Poker.cs --- stack: - int32.600005 diff --git a/testkit/src/test/resources/github/poker/GetCombinationValue_1.sbox b/testkit/src/test/resources/github/poker/GetCombinationValue_1.sbox deleted file mode 100644 index 0592353b..00000000 --- a/testkit/src/test/resources/github/poker/GetCombinationValue_1.sbox +++ /dev/null @@ -1,18 +0,0 @@ -stack: - [int32.0, int32.1, int32.2, int32.3, int32.4, utf8.GetCombinationValue] -storage: - utf8.cardCombinations: bytes.1F2F4F3757673B5B6B733D5D6D75793E5E6E767A7C - utf8.init: "null" -dotnetCompilation: |- - poker.cs ---- -stack: - - int32.1000000 -storage: - utf8.cardCombinations: bytes.1f2f4f3757673b5b6b733d5d6d75793e5e6e767a7c - utf8.init: "null" -effects: - - eventType: StorageRead - program: "0000000000000000000000000000000000000000000000000000000000000000" - key: utf8.init - value: "null" diff --git a/testkit/src/test/resources/github/poker/GetCombinationValue_2.sbox b/testkit/src/test/resources/github/poker/GetCombinationValue_2.sbox deleted file mode 100644 index 1e9702d8..00000000 --- a/testkit/src/test/resources/github/poker/GetCombinationValue_2.sbox +++ /dev/null @@ -1,18 +0,0 @@ -stack: - [int32.0, int32.2, int32.4, int32.6, int32.80, utf8.GetCombinationValue] -storage: - utf8.cardCombinations: bytes.1F2F4F3757673B5B6B733D5D6D75793E5E6E767A7C - utf8.init: "null" -dotnetCompilation: |- - poker.cs ---- -stack: - - int32.50 -storage: - utf8.cardCombinations: bytes.1f2f4f3757673b5b6b733d5d6d75793e5e6e767a7c - utf8.init: "null" -effects: - - eventType: StorageRead - program: "0000000000000000000000000000000000000000000000000000000000000000" - key: utf8.init - value: "null" diff --git a/testkit/src/test/resources/objects/flat-objects.sbox b/testkit/src/test/resources/objects/flat-objects.sbox deleted file mode 100644 index 42f71201..00000000 --- a/testkit/src/test/resources/objects/flat-objects.sbox +++ /dev/null @@ -1,21 +0,0 @@ -stack: - [utf8.Func] -storage: - utf8.init: "null" -dotnetCompilation: |- - objects.cs ---- -stack: - - int32.385 -heap: - ref.0: - utf8.AVal: int32.100 - ref.1: - utf8.BVal: int32.200 -storage: - utf8.init: "null" -effects: - - eventType: StorageRead - program: "0000000000000000000000000000000000000000000000000000000000000000" - key: utf8.init - value: "null" diff --git a/testkit/src/test/resources/smart_program.generated.cs b/testkit/src/test/resources/smart_program.generated.cs deleted file mode 100644 index 9c28379a..00000000 --- a/testkit/src/test/resources/smart_program.generated.cs +++ /dev/null @@ -1,92 +0,0 @@ -using System; -using System.Collections; -using UnityEngine; -using UnityEngine.Networking; - -namespace Expload.Pravda.Program { - [System.Serializable] - class IntResult { - public int value; - public string tpe; - - public static IntResult FromJson(string json) { - return JsonUtility.FromJson(json); - } - } - - abstract class ProgramRequest - { - public byte[] ProgramAddress { get; protected set; } - - public T Result { get; protected set; } - public string Error { get; protected set; } - public bool IsError { get; protected set; } - - protected ProgramRequest(byte[] programAddress) - { - ProgramAddress = programAddress; - IsError = false; - Error = ""; - } - - protected abstract T ParseResult(string json); - - protected IEnumerator SendJson(string json) - { - UnityWebRequest www = UnityWebRequest.Put("localhost:8087/api/program/method", json); - www.method = "POST"; - www.SetRequestHeader("Content-Type", "application/json"); - - yield return www.SendWebRequest(); - - if (www.isNetworkError || www.isHttpError) - { - IsError = true; - Error = www.error; - } - else - { - try - { - Result = ParseResult(www.downloadHandler.text); - } - catch (ArgumentException e) - { - IsError = true; - Error = "Invalid JSON: " + www.downloadHandler.text + "\n" + e.Message; - } - } - } - } - - class BalanceOfRequest: ProgramRequest { - - public BalanceOfRequest(byte[] programAddress) : base(programAddress) { } - - protected override int ParseResult(string json) - { - return IntResult.FromJson(json).value; - } - - public IEnumerator BalanceOf(byte[] arg0) - { - String json = String.Format("{{ \"address\": {0}, \"method\": \"balanceOf\", \"args\": [{{ \"value\": {1}, \"tpe\": \"bytes\" }}] }}", "\"" + BitConverter.ToString(ProgramAddress).Replace("-","") + "\"" , "\"" + BitConverter.ToString(arg0).Replace("-","") + "\"" ); - yield return SendJson(json); - } - } - class TransferRequest: ProgramRequest { - - public TransferRequest(byte[] programAddress) : base(programAddress) { } - - protected override object ParseResult(string json) - { - return null; - } - - public IEnumerator Transfer(byte[] arg0, int arg1) - { - String json = String.Format("{{ \"address\": {0}, \"method\": \"transfer\", \"args\": [{{ \"value\": {1}, \"tpe\": \"bytes\" }}, {{ \"value\": {2}, \"tpe\": \"int32\" }}] }}", "\"" + BitConverter.ToString(ProgramAddress).Replace("-","") + "\"" , "\"" + BitConverter.ToString(arg0).Replace("-","") + "\"" , arg1); - yield return SendJson(json); - } - } -} \ No newline at end of file diff --git a/testkit/src/test/resources/smart_program/balanceOfNoAddress.sbox b/testkit/src/test/resources/smart_program/balanceOfNoAddress.sbox deleted file mode 100644 index 20b9e817..00000000 --- a/testkit/src/test/resources/smart_program/balanceOfNoAddress.sbox +++ /dev/null @@ -1,19 +0,0 @@ -stack: - [bytes.0011, utf8.balanceOf] -storage: - utf8.init: "null" -dotnetCompilation: |- - smart_program.cs ---- -stack: - [int32.0] -storage: - utf8.init: "null" -effects: - - eventType: StorageRead - program: "0000000000000000000000000000000000000000000000000000000000000000" - key: utf8.init - value: "null" - - eventType: StorageRead - program: "0000000000000000000000000000000000000000000000000000000000000000" - key: bytes.62616c616e6365730011 \ No newline at end of file diff --git a/testkit/src/test/resources/smart_program/balanceOfNoCtor.sbox b/testkit/src/test/resources/smart_program/balanceOfNoCtor.sbox deleted file mode 100644 index 3a250526..00000000 --- a/testkit/src/test/resources/smart_program/balanceOfNoCtor.sbox +++ /dev/null @@ -1,26 +0,0 @@ -stack: - [bytes.0011, utf8.balanceOf] -storage: - utf8.init: "null" - bytes.62616C616E6365730011: int32.10 -dotnetCompilation: |- - smart_program.cs ---- -stack: - [int32.10] -storage: - bytes.62616c616e6365730011: int32.10 - utf8.init: "null" -effects: - - eventType: StorageRead - program: "0000000000000000000000000000000000000000000000000000000000000000" - key: utf8.init - value: "null" - - eventType: StorageRead - program: "0000000000000000000000000000000000000000000000000000000000000000" - key: bytes.62616c616e6365730011 - value: int32.10 - - eventType: StorageRead - program: "0000000000000000000000000000000000000000000000000000000000000000" - key: bytes.62616c616e6365730011 - value: int32.10 diff --git a/testkit/src/test/resources/smart_program/balanceOfWithError.sbox b/testkit/src/test/resources/smart_program/balanceOfWithError.sbox deleted file mode 100644 index a044e19d..00000000 --- a/testkit/src/test/resources/smart_program/balanceOfWithError.sbox +++ /dev/null @@ -1,26 +0,0 @@ -stack: - ["null", utf8.balanceOf] -storage: - utf8.init: "null" - bytes.62616C616E6365730011: int32.10 -dotnetCompilation: |- - smart_program.cs ---- -stack: - - "null" - - utf8.balanceOf - - "null" - - bytes.62616c616e636573 - - "null" - - int32.0 -storage: - bytes.62616c616e6365730011: int32.10 - utf8.init: "null" -effects: - - eventType: StorageRead - program: "0000000000000000000000000000000000000000000000000000000000000000" - key: utf8.init - value: "null" -error: - code: 701 - message: "Unexpected type: Null" \ No newline at end of file diff --git a/testkit/src/test/resources/smart_program/wrongMethod.sbox b/testkit/src/test/resources/smart_program/wrongMethod.sbox deleted file mode 100644 index 6bc0b7f6..00000000 --- a/testkit/src/test/resources/smart_program/wrongMethod.sbox +++ /dev/null @@ -1,19 +0,0 @@ -stack: - [utf8.boom] -storage: - utf8.init: "null" -dotnetCompilation: |- - smart_program.cs ---- -stack: - [utf8.boom] -storage: - utf8.init: "null" -effects: - - eventType: StorageRead - program: "0000000000000000000000000000000000000000000000000000000000000000" - key: utf8.init - value: "null" -error: - code: 700 - message: Wrong method name diff --git a/testkit/src/test/resources/stdlib/scall-ed25519-verify-false.sbox b/testkit/src/test/resources/stdlib/scall-ed25519-verify-false.sbox deleted file mode 100644 index 5b6d36f8..00000000 --- a/testkit/src/test/resources/stdlib/scall-ed25519-verify-false.sbox +++ /dev/null @@ -1,19 +0,0 @@ -stack: - - bytes.5877667B812E22A18CF4555D0E5982E5584606214C26B47C967A19C9720E67E3 - - utf8.hello world - - bytes.D1C4F938E388EA5D4F19B939ABD4B0B2DDA3D8F447A34E1B782E36BDC8C65E6368E712166907396C37F6C0B6E28E6F61AD36933142D0B1AB69E63EC9F4300104 - - utf8.ValidateEd25519Signature -storage: - utf8.init: "null" -dotnetCompilation: |- - stdlib.cs ---- -stack: - - bool.false -storage: - utf8.init: "null" -effects: - - eventType: StorageRead - program: "0000000000000000000000000000000000000000000000000000000000000000" - key: utf8.init - value: "null" diff --git a/testkit/src/test/resources/stdlib/scall-ed25519-verify.sbox b/testkit/src/test/resources/stdlib/scall-ed25519-verify.sbox deleted file mode 100644 index 3bfe4774..00000000 --- a/testkit/src/test/resources/stdlib/scall-ed25519-verify.sbox +++ /dev/null @@ -1,19 +0,0 @@ -stack: - - bytes.5877667B812E22A18CF4555D0E5982E5584606214C26B47C967A19C9720E67E3 - - utf8.hello world - - bytes.C9C4F938E388EA5D4F19B939ABD4B0B2DDA3D8F447A34E1B782E36BDC8C65E6368E712166907396C37F6C0B6E28E6F61AD36933142D0B1AB69E63EC9F4300104 - - utf8.ValidateEd25519Signature -storage: - utf8.init: "null" -dotnetCompilation: |- - stdlib.cs ---- -stack: - - bool.true -storage: - utf8.init: "null" -effects: - - eventType: StorageRead - program: "0000000000000000000000000000000000000000000000000000000000000000" - key: utf8.init - value: "null" \ No newline at end of file diff --git a/testkit/src/test/resources/stdlib/scall-ripemd160.sbox b/testkit/src/test/resources/stdlib/scall-ripemd160.sbox deleted file mode 100644 index 7504e691..00000000 --- a/testkit/src/test/resources/stdlib/scall-ripemd160.sbox +++ /dev/null @@ -1,17 +0,0 @@ -stack: - - utf8.hello world - - utf8.Ripemd160 -storage: - utf8.init: "null" -dotnetCompilation: |- - stdlib.cs ---- -stack: - - bytes.98c615784ccb5fe5936fbc0cbe9dfdb408d92f0f -storage: - utf8.init: "null" -effects: - - eventType: StorageRead - program: "0000000000000000000000000000000000000000000000000000000000000000" - key: utf8.init - value: "null" diff --git a/testkit/src/test/resources/vm/pcall.sbox b/testkit/src/test/resources/vm/pcall.sbox deleted file mode 100644 index 5ca5fb63..00000000 --- a/testkit/src/test/resources/vm/pcall.sbox +++ /dev/null @@ -1,63 +0,0 @@ -stack: - [utf8.pcall] -storage: - utf8.init: "null" -program-storage: - "1eaed20b7ce2b336043e4b340b031f95bb1ce6d935ef733ae4df1b66e1e3d91f": - utf8.init: "null" -programs: - "1eaed20b7ce2b336043e4b340b031f95bb1ce6d935ef733ae4df1b66e1e3d91f": bytes.a1ff0b4343494c12110b4463746f7284a1010b4b6d6574686f645f63746f72110f011b02110b44696e697453a1010b476d6574686f6473110f006602110b5b50726f6772616d20776173206e6f7420696e697469616c697a6564a9a1000b476d6574686f647312110b4341646484a1010b4a6d6574686f645f416464110f00a402110b5157726f6e67206d6574686f64206e616d65a9a1000b4a6d6574686f645f416464a1020d440b446e616d650b4341646403010103030001030b4972657475726e54706501031100110304131103041360110302151011030113a1010b474164645f6c7663110f00f701a1000b474164645f6c76631410141014101410a1010b4473746f70110f01c101a1000b4b6d6574686f645f63746f72a1020d420b446e616d650b4463746f720b4972657475726e5470650100110b44696e69745380a1010b4763746f725f6f6b110f018402110b6450726f6772616d20686173206265656e20616c726561647920696e697469616c697a6564a9a1000b4763746f725f6f6b1100110b44696e697450a1010b4863746f725f6c7663110f01ab01a1000b4863746f725f6c766310a1010b4473746f70110f01c101a1000b4473746f70 -dotnetCompilation: |- - pcall.cs pcall_program.dll - Expload.Pravda.Programs.MyProgram ---- -stack: - - int32.10 -heap: - ref.0: - - int8 - - '30' - - '-82' - - '-46' - - '11' - - '124' - - '-30' - - '-77' - - '54' - - '4' - - '62' - - '75' - - '52' - - '11' - - '3' - - '31' - - '-107' - - '-69' - - '28' - - '-26' - - '-39' - - '53' - - '-17' - - '115' - - '58' - - '-28' - - '-33' - - '27' - - '102' - - '-31' - - '-29' - - '-39' - - '31' -effects: - - eventType: StorageRead - program: '0000000000000000000000000000000000000000000000000000000000000000' - key: utf8.init - value: 'null' - - eventType: StorageRead - program: 1eaed20b7ce2b336043e4b340b031f95bb1ce6d935ef733ae4df1b66e1e3d91f - key: utf8.init - value: 'null' - - eventType: StorageRead - program: 1eaed20b7ce2b336043e4b340b031f95bb1ce6d935ef733ae4df1b66e1e3d91f - key: utf8.init - value: 'null' \ No newline at end of file diff --git a/testkit/src/test/resources/vm/throw.sbox b/testkit/src/test/resources/vm/throw.sbox deleted file mode 100644 index 38e3e2e2..00000000 --- a/testkit/src/test/resources/vm/throw.sbox +++ /dev/null @@ -1,17 +0,0 @@ -stack: [utf8.Throw] -storage: - utf8.init: "null" -dotnetCompilation: |- - vm_ops.cs ---- -stack: [utf8.Throw] -storage: - utf8.init: "null" -effects: - - eventType: StorageRead - program: "0000000000000000000000000000000000000000000000000000000000000000" - key: utf8.init - value: "null" -error: - code: 700 - message: Oops! \ No newline at end of file diff --git a/testkit/src/test/resources/zoo_program.generated.cs b/testkit/src/test/resources/zoo_program.generated.cs deleted file mode 100644 index 87213055..00000000 --- a/testkit/src/test/resources/zoo_program.generated.cs +++ /dev/null @@ -1,146 +0,0 @@ -using System; -using System.Collections; -using UnityEngine; -using UnityEngine.Networking; - -namespace Expload.Pravda.Program { - [System.Serializable] - class StringResult { - public string value; - public string tpe; - - public static StringResult FromJson(string json) { - return JsonUtility.FromJson(json); - } - } - [System.Serializable] - class IntResult { - public int value; - public string tpe; - - public static IntResult FromJson(string json) { - return JsonUtility.FromJson(json); - } - } - - abstract class ProgramRequest - { - public byte[] ProgramAddress { get; protected set; } - - public T Result { get; protected set; } - public string Error { get; protected set; } - public bool IsError { get; protected set; } - - protected ProgramRequest(byte[] programAddress) - { - ProgramAddress = programAddress; - IsError = false; - Error = ""; - } - - protected abstract T ParseResult(string json); - - protected IEnumerator SendJson(string json) - { - UnityWebRequest www = UnityWebRequest.Put("localhost:8087/api/program/method", json); - www.method = "POST"; - www.SetRequestHeader("Content-Type", "application/json"); - - yield return www.SendWebRequest(); - - if (www.isNetworkError || www.isHttpError) - { - IsError = true; - Error = www.error; - } - else - { - try - { - Result = ParseResult(www.downloadHandler.text); - } - catch (ArgumentException e) - { - IsError = true; - Error = "Invalid JSON: " + www.downloadHandler.text + "\n" + e.Message; - } - } - } - } - - class BreedPetsRequest: ProgramRequest { - - public BreedPetsRequest(byte[] programAddress) : base(programAddress) { } - - protected override string ParseResult(string json) - { - return StringResult.FromJson(json).value; - } - - public IEnumerator BreedPets(string arg0, string arg1) - { - String json = String.Format("{{ \"address\": {0}, \"method\": \"BreedPets\", \"args\": [{{ \"value\": {1}, \"tpe\": \"utf8\" }}, {{ \"value\": {2}, \"tpe\": \"utf8\" }}] }}", "\"" + BitConverter.ToString(ProgramAddress).Replace("-","") + "\"" , "\"" + arg0 + "\"" , "\"" + arg1 + "\"" ); - yield return SendJson(json); - } - } - class NewPetRequest: ProgramRequest { - - public NewPetRequest(byte[] programAddress) : base(programAddress) { } - - protected override string ParseResult(string json) - { - return StringResult.FromJson(json).value; - } - - public IEnumerator NewPet(int arg0) - { - String json = String.Format("{{ \"address\": {0}, \"method\": \"NewPet\", \"args\": [{{ \"value\": {1}, \"tpe\": \"int32\" }}] }}", "\"" + BitConverter.ToString(ProgramAddress).Replace("-","") + "\"" , arg0); - yield return SendJson(json); - } - } - class NewZooRequest: ProgramRequest { - - public NewZooRequest(byte[] programAddress) : base(programAddress) { } - - protected override int ParseResult(string json) - { - return IntResult.FromJson(json).value; - } - - public IEnumerator NewZoo() - { - String json = String.Format("{{ \"address\": {0}, \"method\": \"NewZoo\", \"args\": [] }}", "\"" + BitConverter.ToString(ProgramAddress).Replace("-","") + "\"" ); - yield return SendJson(json); - } - } - class TransferPetRequest: ProgramRequest { - - public TransferPetRequest(byte[] programAddress) : base(programAddress) { } - - protected override object ParseResult(string json) - { - return null; - } - - public IEnumerator TransferPet(byte[] arg0, int arg1, string arg2) - { - String json = String.Format("{{ \"address\": {0}, \"method\": \"TransferPet\", \"args\": [{{ \"value\": {1}, \"tpe\": \"bytes\" }}, {{ \"value\": {2}, \"tpe\": \"int32\" }}, {{ \"value\": {3}, \"tpe\": \"utf8\" }}] }}", "\"" + BitConverter.ToString(ProgramAddress).Replace("-","") + "\"" , "\"" + BitConverter.ToString(arg0).Replace("-","") + "\"" , arg1, "\"" + arg2 + "\"" ); - yield return SendJson(json); - } - } - class TransferZooRequest: ProgramRequest { - - public TransferZooRequest(byte[] programAddress) : base(programAddress) { } - - protected override object ParseResult(string json) - { - return null; - } - - public IEnumerator TransferZoo(byte[] arg0, int arg1) - { - String json = String.Format("{{ \"address\": {0}, \"method\": \"TransferZoo\", \"args\": [{{ \"value\": {1}, \"tpe\": \"bytes\" }}, {{ \"value\": {2}, \"tpe\": \"int32\" }}] }}", "\"" + BitConverter.ToString(ProgramAddress).Replace("-","") + "\"" , "\"" + BitConverter.ToString(arg0).Replace("-","") + "\"" , arg1); - yield return SendJson(json); - } - } -} \ No newline at end of file diff --git a/testkit/src/test/scala/pravda/testkit/DotnetSandboxSuite.scala b/testkit/src/test/scala/pravda/testkit/DotnetSandboxSuite.scala new file mode 100644 index 00000000..eb6895df --- /dev/null +++ b/testkit/src/test/scala/pravda/testkit/DotnetSandboxSuite.scala @@ -0,0 +1,53 @@ +package pravda.testkit + +import java.io.File + +import com.google.protobuf.ByteString +import org.json4s.DefaultFormats +import pravda.common.json._ +import pravda.dotnet.DotnetCompilation +import pravda.dotnet.translation.Translator +import pravda.plaintest._ +import pravda.vm +import pravda.vm.Data.Primitive +import pravda.vm._ +import pravda.vm.asm.PravdaAssembler +import pravda.vm.json._ + +object DotnetSuiteData { + final case class Preconditions(vm: VmSandbox.Preconditions, `dotnet-compilation`: DotnetCompilation) +} + +import pravda.testkit.DotnetSuiteData._ + +object DotnetSuite extends Plaintest[Preconditions, VmSandbox.ExpectationsWithoutWatts] { + + lazy val dir = new File("testkit/src/test/resources") + override lazy val ext = "sbox" + override lazy val formats = + DefaultFormats + + json4sFormat[Data] + + json4sFormat[Primitive] + + json4sFormat[Primitive.Int64] + + json4sFormat[Primitive.Bytes] + + json4sFormat[vm.Effect] + + json4sFormat[vm.Error] + + json4sFormat[ByteString] + + json4sKeyFormat[ByteString] + + json4sKeyFormat[Primitive.Ref] + + json4sKeyFormat[Primitive] + + def produce(input: Preconditions): Either[String, VmSandbox.ExpectationsWithoutWatts] = { + import pravda.dotnet.clearPathsInPdb + val codeE = + for { + files <- DotnetCompilation.run(input.`dotnet-compilation`) + clearedFiles = clearPathsInPdb(files) + ops <- Translator.translateAsm(clearedFiles, input.`dotnet-compilation`.`main-class`).left.map(_.mkString) + } yield PravdaAssembler.assemble(ops, saveLabels = true) + + for { + code <- codeE + } yield VmSandbox.ExpectationsWithoutWatts.fromExpectations(VmSandbox.run(input.vm, code)) + } +} diff --git a/testkit/src/test/scala/pravda/testkit/DotnetSuite.scala b/testkit/src/test/scala/pravda/testkit/DotnetSuite.scala deleted file mode 100644 index eab48028..00000000 --- a/testkit/src/test/scala/pravda/testkit/DotnetSuite.scala +++ /dev/null @@ -1,189 +0,0 @@ -package pravda.testkit - -import java.io.File -import java.nio.file.{Files, Paths} - -import pravda.dotnet.parser.FileParser -import pravda.dotnet.parser.FileParser.ParsedDotnetFile -import pravda.dotnet.translation.Translator -import pravda.plaintest._ -import pravda.vm._ -import cats.instances.list._ -import cats.instances.either._ -import cats.syntax.traverse._ -import com.google.protobuf.ByteString -import org.json4s.DefaultFormats -import pravda.common.domain.Address -import pravda.common.json._ -import pravda.vm -import pravda.vm.Data.Primitive -import pravda.vm.Error.DataError -import pravda.vm.asm.PravdaAssembler -import pravda.vm.impl.{MemoryImpl, VmImpl, WattCounterImpl} -import pravda.vm.json._ - -import scala.collection.mutable -import scala.collection.mutable.ArrayBuffer -import scala.util.Try - -object DotnetSuiteData { - final case class Preconditions(balances: Map[Address, Primitive.BigInt] = Map.empty, - stack: Seq[Primitive] = Nil, - heap: Map[Primitive.Ref, Data] = Map.empty, - storage: Map[Primitive, Data] = Map.empty, - `program-storage`: Map[Address, Map[Primitive, Data]] = Map.empty, - programs: Map[Address, Primitive.Bytes] = Map.empty, - executor: Option[Address] = None, - dotnetCompilation: String) - - final case class Expectations(stack: Seq[Primitive] = Nil, - heap: Map[Primitive.Ref, Data] = Map.empty, - effects: Seq[vm.Effect] = Nil, - error: Option[vm.Error] = None) -} - -import DotnetSuiteData._ - -object DotnetSuite extends Plaintest[Preconditions, Expectations] { - - private def dotnetToAsm(filename: String, - dllsFiles: List[String], - mainClass: Option[String]): Either[String, List[asm.Operation]] = { - import scala.sys.process._ - - val exploadDll = new File("PravdaDotNet/Pravda.dll") - - new File("/tmp/pravda/").mkdirs() - - val tmpSrcs = - (filename :: dllsFiles).map(f => (new File(s"dotnet-tests/resources/$f"), new File(s"/tmp/pravda/$f"))) - - tmpSrcs.foreach { - case (from, dest) => - if (!dest.exists()) { - Files.copy(from.toPath, dest.toPath) - } - } - - val exe = File.createTempFile("dotnet-", ".exe") - val pdb = File.createTempFile("dotnet-", ".pdb") - s"""csc ${tmpSrcs.head._2.getAbsolutePath} - |-out:${exe.getAbsolutePath} - |-reference:${exploadDll.getAbsolutePath} - |${tmpSrcs.tail.map(dll => s"-reference:${dll._2.getAbsolutePath}").mkString("\n")} - |-debug:portable - |-pdb:${pdb.getAbsolutePath} - """.stripMargin.!! - - for { - pe <- FileParser.parsePe(Files.readAllBytes(exe.toPath)) - pdb <- FileParser.parsePdb(Files.readAllBytes(pdb.toPath)) - dlls <- dllsFiles - .map(f => FileParser.parsePe(Files.readAllBytes(Paths.get(s"dotnet-tests/resources/$f")))) - .sequence - asm <- Translator - .translateAsm(ParsedDotnetFile(pe, Some(pdb)) :: dlls.map(dll => ParsedDotnetFile(dll, None)), mainClass) - .left - .map(_.mkString) - } yield asm - } - - lazy val dir = new File("testkit/src/test/resources") - override lazy val ext = "sbox" - override lazy val formats = - DefaultFormats + - json4sFormat[Data] + - json4sFormat[Primitive] + - json4sFormat[Primitive.BigInt] + - json4sFormat[Primitive.Bytes] + - json4sFormat[vm.Effect] + - json4sFormat[vm.Error] + - json4sKeyFormat[ByteString] + - json4sKeyFormat[Primitive.Ref] + - json4sKeyFormat[Primitive] - - def produce(input: Preconditions): Either[String, Expectations] = { - val lines = input.dotnetCompilation.lines.toList - val file :: dlls = lines.head.split("\\s+").toList - val mainClass = lines.tail.headOption - val code = dotnetToAsm(file, dlls, mainClass) - val asmProgram = code.map(c => PravdaAssembler.assemble(c, saveLabels = true)) - - val sandboxVm = new VmImpl() - val heap = { - if (input.heap.nonEmpty) { - val length = input.heap.map(_._1.data).max + 1 - val buffer = ArrayBuffer.fill[Data](length)(Data.Primitive.Null) - input.heap.foreach { case (ref, value) => buffer(ref.data) = value } - buffer - } else { - ArrayBuffer[Data]() - } - } - val memory = MemoryImpl(ArrayBuffer(input.stack: _*), heap) - val wattCounter = new WattCounterImpl(Long.MaxValue) - - val pExecutor = input.executor.getOrElse { - Address @@ ByteString.copyFrom((1 to 32).map(_.toByte).toArray) - } - - val effects = mutable.Buffer[vm.Effect]() - val environment: Environment = new VmSandbox.EnvironmentSandbox( - effects = effects, - initStorages = input.`program-storage`, - initBalances = input.balances.toSeq, - initPrograms = input.programs.toSeq, - pExecutor = pExecutor - ) - - val storage = new VmSandbox.StorageSandbox( - Address.Void, - effects, - input.storage.toSeq - ) - - for { - a <- asmProgram - } yield { - val error = Try { - memory.enterProgram(Address.Void) - sandboxVm.runBytes( - a.asReadOnlyByteBuffer(), - environment, - memory, - wattCounter, - Some(storage), - Some(Address.Void), - pcallAllowed = true - ) - memory.exitProgram() - }.fold( - { - case e: Data.DataException => - Some( - RuntimeException( - DataError(e.getMessage), - FinalState(wattCounter.spent, wattCounter.refund, wattCounter.total, memory.stack, memory.heap), - memory.callStack, - memory.currentCounter - )) - case ThrowableVmError(e) => - Some( - RuntimeException( - e, - FinalState(wattCounter.spent, wattCounter.refund, wattCounter.total, memory.stack, memory.heap), - memory.callStack, - memory.currentCounter)) - }, - _ => None - ) - - Expectations( - memory.stack, - memory.heap.zipWithIndex.map { case (d, i) => Data.Primitive.Ref(i) -> d }.toMap, - effects, - error.map(_.error) - ) - } - } -} diff --git a/testkit/src/test/scala/pravda/testkit/DotnetToCodegen.scala b/testkit/src/test/scala/pravda/testkit/DotnetToCodegen.scala index 165901e9..72b421ca 100644 --- a/testkit/src/test/scala/pravda/testkit/DotnetToCodegen.scala +++ b/testkit/src/test/scala/pravda/testkit/DotnetToCodegen.scala @@ -1,11 +1,8 @@ package pravda.testkit -import java.io.File -import java.nio.file.Files - import pravda.codegen.dotnet.DotnetCodegen import pravda.common.TestUtils -import pravda.dotnet.parser.FileParser +import pravda.dotnet.DotnetCompilation.dsl._ import pravda.dotnet.parser.FileParser.ParsedDotnetFile import pravda.dotnet.translation.Translator import pravda.vm.asm.PravdaAssembler @@ -16,22 +13,30 @@ import scala.io.Source object DotnetToCodegen extends TestSuite { val tests = Tests { - 'smart_program - { - val Right(pe) = - FileParser.parsePe(Files.readAllBytes(new File(getClass.getResource("/smart_program.exe").getPath).toPath)) - val Right(asm) = Translator.translateAsm(List(ParsedDotnetFile(pe, None)), None) - val unityMethods = DotnetCodegen.generate(PravdaAssembler.assemble(asm, false)) - - TestUtils.assertEqual(unityMethods, ("Program.cs", Source.fromResource("smart_program.generated.cs").mkString)) + 'SmartProgram - { + val Right(files) = + steps( + "Pravda.dll" -> Seq("PravdaDotNet/Pravda.cs"), + "SmartProgram.exe" -> Seq("Pravda.dll", "dotnet-tests/resources/SmartProgram.cs") + ).run + + val Right(asm) = Translator.translateAsm(List(ParsedDotnetFile(files.last.parsedPe, None)), None) + val unityMethods = DotnetCodegen.generate(PravdaAssembler.assemble(asm, false)).head + + TestUtils.assertEqual(unityMethods, + ("SmartProgram.cs", Source.fromResource("SmartProgram.generated.cs").mkString)) } - 'zoo_program - { - val Right(pe) = - FileParser.parsePe(Files.readAllBytes(new File(getClass.getResource("/zoo_program.exe").getPath).toPath)) - val Right(asm) = Translator.translateAsm(List(ParsedDotnetFile(pe, None)), None) - val unityMethods = DotnetCodegen.generate(PravdaAssembler.assemble(asm, false)) + 'ZooProgram - { + val Right(files) = + steps( + "Pravda.dll" -> Seq("PravdaDotNet/Pravda.cs"), + "ZooProgram.exe" -> Seq("Pravda.dll", "dotnet-tests/resources/ZooProgram.cs") + ).run - TestUtils.assertEqual(unityMethods, ("Program.cs", Source.fromResource("zoo_program.generated.cs").mkString)) + val Right(asm) = Translator.translateAsm(List(ParsedDotnetFile(files.last.parsedPe, None)), None) + val unityMethods = DotnetCodegen.generate(PravdaAssembler.assemble(asm, false)).head + TestUtils.assertEqual(unityMethods, ("ZooProgram.cs", Source.fromResource("ZooProgram.generated.cs").mkString)) } } } diff --git a/vm-api/src/main/scala/pravda/vm/Data.scala b/vm-api/src/main/scala/pravda/vm/Data.scala index 77c11af3..89f0c428 100644 --- a/vm-api/src/main/scala/pravda/vm/Data.scala +++ b/vm-api/src/main/scala/pravda/vm/Data.scala @@ -17,7 +17,7 @@ package pravda.vm -import java.nio.ByteBuffer +import java.nio.{ByteBuffer, ByteOrder} import java.nio.charset.StandardCharsets import com.google.protobuf.ByteString @@ -49,9 +49,7 @@ import scala.{Array => ScalaArray, BigInt => ScalaBigInt} case Int8(_) => "Int8" case Int16(_) => "Int16" case Int32(_) => "Int32" - case Uint8(_) => "Uint8" - case Uint16(_) => "Uint16" - case Uint32(_) => "Uint32" + case Int64(_) => "Int64" case BigInt(_) => "BigInt" case Number(_) => "Number" case Utf8(_) => "Utf8" @@ -61,9 +59,7 @@ import scala.{Array => ScalaArray, BigInt => ScalaBigInt} case Int8Array(_) => "Int8Array" case Int16Array(_) => "Int16Array" case Int32Array(_) => "Int32Array" - case Uint8Array(_) => "Uint8Array" - case Uint16Array(_) => "Uint16Array" - case Uint32Array(_) => "Uint32Array" + case Int64Array(_) => "Int64Array" case BigIntArray(_) => "BigIntArray" case NumberArray(_) => "NumberArray" case RefArray(_) => "RefArray" @@ -112,9 +108,7 @@ import scala.{Array => ScalaArray, BigInt => ScalaBigInt} case Int8(data) => s"int8($data)" case Int16(data) => s"int16($data)" case Int32(data) => s"int32($data)" - case Uint8(data) => s"uint8($data)" - case Uint16(data) => s"uint16($data)" - case Uint32(data) => s"uint32($data)" + case Int64(data) => s"int64($data)" case Number(data) => s"number($data)" case BigInt(data) => s"bigint($data)" case Ref(data) => s"#${ref(data)}" @@ -126,9 +120,7 @@ import scala.{Array => ScalaArray, BigInt => ScalaBigInt} case Int8Array(data) => s"int8[${array(data)}]" case Int16Array(data) => s"int16[${array(data)}]" case Int32Array(data) => s"int32[${array(data)}]" - case Uint8Array(data) => s"uint8[${array(data)}]" - case Uint16Array(data) => s"uint16[${array(data)}]" - case Uint32Array(data) => s"uint32[${array(data)}]" + case Int64Array(data) => s"int64[${array(data)}]" case NumberArray(data) => s"number[${array(data)}]" case BigIntArray(data) => s"bigint[${array(data)}]" case RefArray(data) => s"#[${arraym(data, ref)}]" @@ -289,9 +281,7 @@ import scala.{Array => ScalaArray, BigInt => ScalaBigInt} case Int8(data) => putPrimitive(Type.Int8)(_.put(data)) case Int16(data) => putPrimitive(Type.Int16)(_.putShort(data)) case Int32(data) => putPrimitive(Type.Int32)(_.putInt(data)) - case Uint8(data) => putPrimitive(Type.Uint8)(_.putInt(data)) - case Uint16(data) => putPrimitive(Type.Uint16)(_.putInt(data)) - case Uint32(data) => putPrimitive(Type.Uint32)(_.putLong(data)) + case Int64(data) => putPrimitive(Type.Int64)(_.putLong(data)) case BigInt(data) => putTaggedBigInt(buffer, data) case Ref(data) => putRef(data) case Offset(data) => putOffset(data) @@ -300,9 +290,7 @@ import scala.{Array => ScalaArray, BigInt => ScalaBigInt} case Int8Array(data) => putPrimitiveArray(Type.Int8, data)(_.put(_)) case Int16Array(data) => putPrimitiveArray(Type.Int16, data)(_.putShort(_)) case Int32Array(data) => putPrimitiveArray(Type.Int32, data)(_.putInt(_)) - case Uint8Array(data) => putPrimitiveArray(Type.Uint8, data)(_.putInt(_)) - case Uint16Array(data) => putPrimitiveArray(Type.Uint16, data)(_.putInt(_)) - case Uint32Array(data) => putPrimitiveArray(Type.Uint32, data)(_.putLong(_)) + case Int64Array(data) => putPrimitiveArray(Type.Int64, data)(_.putLong(_)) case BigIntArray(data) => putArray(Type.BigInt, data)(putBigInt) case RefArray(data) => putArray(Type.Ref, data)(_.putInt(_)) case Number(data) => putPrimitive(Type.Number)(_.putDouble(data)) @@ -345,9 +333,7 @@ import scala.{Array => ScalaArray, BigInt => ScalaBigInt} case Type.Int8 => value case Type.Int16 => Int16(data.toShort) case Type.Int32 => Int32(data.toInt) - case Type.Uint8 => Uint8(data & 0xFF) - case Type.Uint16 => Uint16(data & 0xFF) - case Type.Uint32 => Uint32(data & 0xFFl) + case Type.Int64 => Int64(data.toLong) case Type.BigInt => BigInt(scala.BigInt(data.toInt)) case Type.Number => Number(data.toDouble) case Type.Ref => Ref(data.toInt) @@ -363,9 +349,7 @@ import scala.{Array => ScalaArray, BigInt => ScalaBigInt} case Type.Int8 => Int8(data.toByte) case Type.Int16 => value case Type.Int32 => Int32(data.toInt) - case Type.Uint8 => Uint8(data & 0xFF) - case Type.Uint16 => Uint16(data & 0xFFFF) - case Type.Uint32 => Uint32(data & 0xFFFFl) + case Type.Int64 => Int64(data.toLong) case Type.BigInt => BigInt(scala.BigInt(data.toInt)) case Type.Number => Number(data.toDouble) case Type.Ref => Ref(data.toInt) @@ -373,6 +357,7 @@ import scala.{Array => ScalaArray, BigInt => ScalaBigInt} case Type.Utf8 => Utf8(data.toString) case Type.Bytes => val buffer = ByteBuffer.allocate(2) + buffer.order(ByteOrder.LITTLE_ENDIAN) buffer.putShort(data) buffer.rewind() Bytes(ByteString.copyFrom(buffer)) @@ -382,9 +367,7 @@ import scala.{Array => ScalaArray, BigInt => ScalaBigInt} case Type.Int8 => Int8(data.toByte) case Type.Int16 => Int16(data.toShort) case Type.Int32 => value - case Type.Uint8 => Uint8(data & 0xFFFFFFFF) - case Type.Uint16 => Uint16(data & 0xFFFFFFFF) - case Type.Uint32 => Uint32(data.toLong & 0xFFFFFFFFl) + case Type.Int64 => Int64(data.toLong) case Type.BigInt => BigInt(scala.BigInt(data)) case Type.Number => Number(data.toDouble) case Type.Ref => Ref(data) @@ -392,63 +375,26 @@ import scala.{Array => ScalaArray, BigInt => ScalaBigInt} case Type.Utf8 => Utf8(data.toString) case Type.Bytes => val buffer = ByteBuffer.allocate(4) + buffer.order(ByteOrder.LITTLE_ENDIAN) buffer.putInt(data) buffer.rewind() Bytes(ByteString.copyFrom(buffer)) } - case value @ Uint8(data) => + case value @ Int64(data) => `type` match { case Type.Int8 => Int8(data.toByte) case Type.Int16 => Int16(data.toShort) case Type.Int32 => Int32(data.toInt) - case Type.Uint8 => value - case Type.Uint16 => Uint16(data) - case Type.Uint32 => Uint32(data.toLong) + case Type.Int64 => value case Type.BigInt => BigInt(scala.BigInt(data)) case Type.Number => Number(data.toDouble) case Type.Ref => Ref(data.toInt) case Type.Boolean => if (data == 0) Bool.False else Bool.True case Type.Utf8 => Utf8(data.toString) case Type.Bytes => - val array = new scala.Array[Byte](1) - array(0) = data.toByte - Bytes(ByteString.copyFrom(array)) - } - case value @ Uint16(data) => - `type` match { - case Type.Int8 => Int8(data.toByte) - case Type.Int16 => Int16(data.toShort) - case Type.Int32 => Int32(data.toInt) - case Type.Uint8 => Uint8(data & 0xFF) - case Type.Uint16 => value - case Type.Uint32 => Uint32(data.toLong) - case Type.BigInt => BigInt(scala.BigInt(data)) - case Type.Number => Number(data.toDouble) - case Type.Ref => Ref(data.toInt) - case Type.Boolean => if (data == 0) Bool.False else Bool.True - case Type.Utf8 => Utf8(data.toString) - case Type.Bytes => - val buffer = ByteBuffer.allocate(2) - buffer.putShort(data.toShort) - buffer.rewind() - Bytes(ByteString.copyFrom(buffer)) - } - case value @ Uint32(data) => - `type` match { - case Type.Int8 => Int8(data.toByte) - case Type.Int16 => Int16(data.toShort) - case Type.Int32 => Int32(data.toInt) - case Type.Uint8 => Uint8((data & 0xFF).toInt) - case Type.Uint16 => Uint16((data & 0xFFFF).toInt) - case Type.Uint32 => value - case Type.BigInt => BigInt(scala.BigInt(data)) - case Type.Number => Number(data.toDouble) - case Type.Ref => Ref(data.toInt) - case Type.Boolean => if (data == 0) Bool.False else Bool.True - case Type.Utf8 => Utf8(data.toString) - case Type.Bytes => - val buffer = ByteBuffer.allocate(4) - buffer.putInt(data.toInt) + val buffer = ByteBuffer.allocate(8) + buffer.order(ByteOrder.LITTLE_ENDIAN) + buffer.putLong(data) buffer.rewind() Bytes(ByteString.copyFrom(buffer)) } @@ -457,9 +403,7 @@ import scala.{Array => ScalaArray, BigInt => ScalaBigInt} case Type.Int8 => Int8(data.toByte) case Type.Int16 => Int16(data.toShort) case Type.Int32 => Int32(data) - case Type.Uint8 => Uint8(data & 0xFF) - case Type.Uint16 => Uint16(data & 0xFFFF) - case Type.Uint32 => Uint32(data.toLong & 0xFFFFFFFFl) + case Type.Int64 => Int64(data.toLong) case Type.BigInt => BigInt(scala.BigInt(data)) case Type.Number => Number(data.toDouble) case Type.Ref => value @@ -467,6 +411,7 @@ import scala.{Array => ScalaArray, BigInt => ScalaBigInt} case Type.Utf8 => Utf8(data.toString) case Type.Bytes => val buffer = ByteBuffer.allocate(4) + buffer.order(ByteOrder.LITTLE_ENDIAN) buffer.putInt(data) buffer.rewind() Bytes(ByteString.copyFrom(buffer)) @@ -476,9 +421,7 @@ import scala.{Array => ScalaArray, BigInt => ScalaBigInt} case Type.Int8 => Int8(data.toByte) case Type.Int16 => Int16(data.toShort) case Type.Int32 => Int32(data.toInt) - case Type.Uint8 => Uint8(data.toInt & 0xFF) - case Type.Uint16 => Uint16(data.toInt & 0xFFFF) - case Type.Uint32 => Uint32(data.toLong & 0xFFFFFFFFl) + case Type.Int64 => Int64(data.toLong) case Type.BigInt => BigInt(scala.BigInt(data.toLong)) case Type.Number => value case Type.Ref => Ref(data.toInt) @@ -486,6 +429,7 @@ import scala.{Array => ScalaArray, BigInt => ScalaBigInt} case Type.Utf8 => Utf8(data.toString) case Type.Bytes => val buffer = ByteBuffer.allocate(8) + buffer.order(ByteOrder.LITTLE_ENDIAN) buffer.putDouble(data) buffer.rewind() Bytes(ByteString.copyFrom(buffer)) @@ -495,9 +439,7 @@ import scala.{Array => ScalaArray, BigInt => ScalaBigInt} case Type.Int8 => Int8(data.toByte) case Type.Int16 => Int16(data.toShort) case Type.Int32 => Int32(data.toInt) - case Type.Uint8 => Uint8(data.toInt & 0xFF) - case Type.Uint16 => Uint16(data.toInt & 0xFFFF) - case Type.Uint32 => Uint32(data.toLong & 0xFFFFFFFFl) + case Type.Int64 => Int64(data.toLong) case Type.BigInt => BigInt(scala.BigInt(data.toLong)) case Type.Number => Number(data.toDouble) case Type.Ref => Ref(data.toInt) @@ -507,14 +449,13 @@ import scala.{Array => ScalaArray, BigInt => ScalaBigInt} } case value @ Bytes(data) => lazy val buffer = data.asReadOnlyByteBuffer() + buffer.order(ByteOrder.LITTLE_ENDIAN) `type` match { case Type.Int8 => Int8(buffer.get) case Type.Int16 => Int16(buffer.getShort) case Type.Int32 => Int32(buffer.getInt) - case Type.Uint8 => Uint8(buffer.get & 0xFF) - case Type.Uint16 => Uint16(buffer.getShort & 0xFFFF) - case Type.Uint32 => Uint32(buffer.getInt.toLong & 0xFFFFFFFFl) - case Type.BigInt => BigInt(scala.BigInt(data.toByteArray)) + case Type.Int64 => Int64(buffer.getLong) + case Type.BigInt => BigInt(scala.BigInt(data.toByteArray.reverse)) case Type.Number => Number(buffer.getDouble) case Type.Ref => Ref(buffer.getInt) case Type.Boolean => if (data.isEmpty) Bool.False else Bool.True @@ -526,24 +467,20 @@ import scala.{Array => ScalaArray, BigInt => ScalaBigInt} case Type.Int8 => Int8(data.toByte) case Type.Int16 => Int16(data.toShort) case Type.Int32 => Int32(data.toInt) - case Type.Uint8 => Uint8(data.toInt & 0xFF) - case Type.Uint16 => Uint16(data.toInt & 0xFFFF) - case Type.Uint32 => Uint32(data.toLong & 0xFFFFFFFFl) + case Type.Int64 => Int64(data.toLong) case Type.BigInt => value case Type.Number => Number(data.toDouble) case Type.Ref => Ref(data.toInt) case Type.Boolean => if (data == 0) Bool.False else Bool.True case Type.Utf8 => Utf8(data.toString) - case Type.Bytes => Bytes(ByteString.copyFrom(data.toByteArray)) + case Type.Bytes => Bytes(ByteString.copyFrom(data.toByteArray.reverse)) } case Bool.False => `type` match { case Type.Int8 => Int8(0.toByte) case Type.Int16 => Int16(0.toShort) case Type.Int32 => Int32(0) - case Type.Uint8 => Uint8(0) - case Type.Uint16 => Uint16(0) - case Type.Uint32 => Uint32(0L) + case Type.Int64 => Int64(0L) case Type.BigInt => BigInt(scala.BigInt(0)) case Type.Number => Number(0d) case Type.Boolean => Bool.False @@ -558,9 +495,7 @@ import scala.{Array => ScalaArray, BigInt => ScalaBigInt} case Type.Int8 => Int8(1.toByte) case Type.Int16 => Int16(1.toShort) case Type.Int32 => Int32(1) - case Type.Uint8 => Uint8(1) - case Type.Uint16 => Uint16(1) - case Type.Uint32 => Uint32(1L) + case Type.Int64 => Int64(1L) case Type.BigInt => BigInt(scala.BigInt(1)) case Type.Number => Number(1d) case Type.Boolean => Bool.True @@ -585,14 +520,14 @@ import scala.{Array => ScalaArray, BigInt => ScalaBigInt} final case class Int8(data: Byte) extends Numeric[Byte] final case class Int16(data: Short) extends Numeric[Short] final case class Int32(data: Int) extends Numeric[Int] - final case class Uint8(data: Int) extends Numeric[Int] - final case class Uint16(data: Int) extends Numeric[Int] - final case class Uint32(data: Long) extends Numeric[Long] + final case class Int64(data: Long) extends Numeric[Long] final case class BigInt(data: scala.BigInt) extends Numeric[scala.BigInt] final case class Number(data: Double) extends Numeric[Double] final case class Utf8(data: String) extends Primitive with Array - final case class Bytes(data: ByteString) extends Primitive with Array - final case class Ref(data: Int) extends Primitive + final case class Bytes(data: ByteString) extends Primitive with Array { + override def toString: String = s"Bytes(0x${byteString2hex(data)})" + } + final case class Ref(data: Int) extends Primitive /** * Special primitive to present offsets in program. @@ -634,9 +569,7 @@ import scala.{Array => ScalaArray, BigInt => ScalaBigInt} final case class Int8Array(data: mutable.Buffer[Byte]) extends Array final case class Int16Array(data: mutable.Buffer[Short]) extends Array final case class Int32Array(data: mutable.Buffer[Int]) extends Array - final case class Uint8Array(data: mutable.Buffer[Int]) extends Array - final case class Uint16Array(data: mutable.Buffer[Int]) extends Array - final case class Uint32Array(data: mutable.Buffer[Long]) extends Array + final case class Int64Array(data: mutable.Buffer[Long]) extends Array final case class BigIntArray(data: mutable.Buffer[scala.BigInt]) extends Array final case class NumberArray(data: mutable.Buffer[Double]) extends Array final case class RefArray(data: mutable.Buffer[Int]) extends Array @@ -747,25 +680,21 @@ import scala.{Array => ScalaArray, BigInt => ScalaBigInt} val int8 = P(IgnoreCase("int8(") ~/ ws ~ int ~ ws ~ ")").map(x => Primitive.Int8(x.toByte)) val int16 = P(IgnoreCase("int16(") ~/ ws ~ int ~ ws ~ ")").map(x => Primitive.Int16(x.toShort)) val int32 = P(IgnoreCase("int32(") ~/ ws ~ int ~ ws ~ ")").map(x => Primitive.Int32(x.toInt)) - val uint8 = P(IgnoreCase("uint8(") ~/ ws ~ uint ~ ws ~ ")").map(x => Primitive.Uint8(x.toInt)) - val uint16 = P(IgnoreCase("uint16(") ~/ ws ~ uint ~ ws ~ ")").map(x => Primitive.Uint16(x.toInt)) - val uint32 = P(IgnoreCase("uint32(") ~/ ws ~ uint ~ ws ~ ")").map(x => Primitive.Uint32(x.toLong)) + val int64 = P(IgnoreCase("int64(") ~/ ws ~ int ~ ws ~ ")").map(x => Primitive.Int64(x.toLong)) val bigint = P(IgnoreCase("bigint(") ~/ ws ~ int ~ ws ~ ")").map(x => Primitive.BigInt(x)) val number = P(IgnoreCase("number(") ~/ ws ~ float ~ ws ~ ")").map(x => Primitive.Number(x)) val inferredNumeric = P { float.map(Primitive.Number) | int.map { - case v if v >= 0 && v <= 0xFF => Primitive.Uint8(v.toInt) - case v if v >= 0 && v <= 0xFFFF => Primitive.Uint16(v.toInt) - case v if v >= 0 && v <= 0xFFFFFFFFL => Primitive.Uint32(v.toLong) case v if v >= Byte.MinValue.toInt && v <= Byte.MaxValue.toInt => Primitive.Int8(v.toByte) case v if v >= Short.MinValue.toInt && v <= Short.MaxValue.toInt => Primitive.Int16(v.toShort) case v if v >= Int.MinValue && v <= Int.MaxValue => Primitive.Int32(v.toInt) - case v => Primitive.BigInt(v.toLong) + case v if v >= Long.MinValue && v <= Long.MaxValue => Primitive.Int64(v.toLong) + case v => Primitive.BigInt(v) } } - val numeric = P(number | int8 | int16 | int32 | uint8 | uint16 | uint32 | bigint | inferredNumeric) + val numeric = P(number | int8 | int16 | int32 | int64 | bigint | inferredNumeric) val bool = P( IgnoreCase("true").map(_ => Primitive.Bool.True) | IgnoreCase("false").map(_ => Primitive.Bool.False)) @@ -811,9 +740,7 @@ import scala.{Array => ScalaArray, BigInt => ScalaBigInt} val int8Array = arrayParser("int8", int, int8)(Array.Int8Array, _.toByte, _.data) val int16Array = arrayParser("int16", int, int16)(Array.Int16Array, _.toShort, _.data) val int32Array = arrayParser("int32", int, int32)(Array.Int32Array, _.toInt, _.data) - val uint8Array = arrayParser("uint8", uint, uint8)(Array.Uint8Array, _.toInt, _.data) - val uint16Array = arrayParser("uint16", uint, uint16)(Array.Uint16Array, _.toInt, _.data) - val uint32Array = arrayParser("uint32", uint, uint32)(Array.Uint32Array, _.toLong, _.data) + val int64Array = arrayParser("int64", int, int64)(Array.Int64Array, _.toLong, _.data) val numberArray = arrayParser("number", float, number)(Array.NumberArray, identity, _.data) val bigintArray = arrayParser("bigint", int, bigint)(Array.BigIntArray, identity, _.data) val boolArray = arrayParser("bool", bool, bool)(Array.BoolArray, identity, identity) @@ -822,8 +749,7 @@ import scala.{Array => ScalaArray, BigInt => ScalaBigInt} val bytesArray = arrayParser("x", hexString, bytes)(Array.BytesArray, identity, _.data) val array = P( - int8Array | int16Array | int32Array - | uint8Array | uint16Array | uint32Array + int8Array | int16Array | int32Array | int64Array | bigintArray | numberArray | refArray | boolArray | utf8Array | bytesArray ) @@ -929,9 +855,7 @@ import scala.{Array => ScalaArray, BigInt => ScalaBigInt} else Primitive.Bool.False def getInt16 = primitiveBuffer(2).getShort def getInt32 = primitiveBuffer(4).getInt - def getUint8 = primitiveBuffer(4).getInt & 0xFF - def getUint16 = primitiveBuffer(4).getInt & 0xFFFF - def getUint32: Long = primitiveBuffer(8).getLong & 0xFFFFFFFFl + def getInt64 = primitiveBuffer(8).getLong def getBigInt = scala.BigInt(getBytes(getLength)) def getDouble = primitiveBuffer(8).getDouble def getRef = buffer.getInt() @@ -942,10 +866,8 @@ import scala.{Array => ScalaArray, BigInt => ScalaBigInt} case Type.Int8 => Primitive.Int8(getInt8) // int8 case Type.Int16 => Primitive.Int16(getInt16) // int16 case Type.Int32 => Primitive.Int32(getInt32) // int32 - case Type.Uint8 => Primitive.Uint8(getUint8) // uint8 - case Type.Uint16 => Primitive.Uint16(getUint16) // uint16 - case Type.Uint32 => Primitive.Uint32(getUint32) // uint32 - case Type.BigInt => Primitive.BigInt(getBigInt) // uint64 + case Type.Int64 => Primitive.Int64(getInt64) // int64 + case Type.BigInt => Primitive.BigInt(getBigInt) // bigint case Type.Number => Primitive.Number(getDouble) // decimal case Type.Boolean => getBool case Type.Ref => Primitive.Ref(getRef) @@ -961,10 +883,8 @@ import scala.{Array => ScalaArray, BigInt => ScalaBigInt} case Type.Int8 => Array.Int8Array(mutable.Buffer.fill(l)(getInt8)) case Type.Int16 => Array.Int16Array(mutable.Buffer.fill(l)(getInt16)) case Type.Int32 => Array.Int32Array(mutable.Buffer.fill(l)(getInt32)) + case Type.Int64 => Array.Int64Array(mutable.Buffer.fill(l)(getInt64)) case Type.BigInt => Array.BigIntArray(mutable.Buffer.fill(l)(getBigInt)) - case Type.Uint8 => Array.Uint8Array(mutable.Buffer.fill(l)(getUint8)) - case Type.Uint16 => Array.Uint16Array(mutable.Buffer.fill(l)(getUint16)) - case Type.Uint32 => Array.Uint32Array(mutable.Buffer.fill(l)(getUint32)) case Type.Number => Array.NumberArray(mutable.Buffer.fill(l)(getDouble)) case Type.Boolean => Array.BoolArray(mutable.Buffer.fill(l)(getBool)) case Type.Ref => Array.RefArray(mutable.Buffer.fill(l)(getRef)) @@ -998,9 +918,8 @@ import scala.{Array => ScalaArray, BigInt => ScalaBigInt} final val Int16 = Type @@ 0x02.toByte final val Int32 = Type @@ 0x03.toByte final val BigInt = Type @@ 0x04.toByte - final val Uint8 = Type @@ 0x05.toByte - final val Uint16 = Type @@ 0x06.toByte - final val Uint32 = Type @@ 0x07.toByte + final val Int64 = Type @@ 0x05.toByte + // free 0x06, 0x07 final val Number = Type @@ 0x08.toByte final val Boolean = Type @@ 0x09.toByte final val Ref = Type @@ 0x0A.toByte diff --git a/vm-api/src/main/scala/pravda/vm/Error.scala b/vm-api/src/main/scala/pravda/vm/Error.scala index a505d55d..fba20326 100644 --- a/vm-api/src/main/scala/pravda/vm/Error.scala +++ b/vm-api/src/main/scala/pravda/vm/Error.scala @@ -21,6 +21,8 @@ sealed abstract class Error(val code: Int) object Error { + case object NoInfoAboutAppState extends Error(50) + case object StackOverflow extends Error(100) case object StackUnderflow extends Error(101) case object WrongStackIndex extends Error(102) diff --git a/vm-api/src/main/scala/pravda/vm/Meta.scala b/vm-api/src/main/scala/pravda/vm/Meta.scala index 27a8742b..4d6c0d01 100644 --- a/vm-api/src/main/scala/pravda/vm/Meta.scala +++ b/vm-api/src/main/scala/pravda/vm/Meta.scala @@ -87,10 +87,8 @@ object Meta { case Int8 => "int8" case Int16 => "int16" case Int32 => "int32" + case Int64 => "int64" case BigInt => "bigint" - case Uint8 => "uint8" - case Uint16 => "uint16" - case Uint32 => "uint32" case Number => "number" case Boolean => "boolean" case Ref => "ref" @@ -107,10 +105,8 @@ object Meta { case object Int8 extends PrimitiveType(Data.Type.Int8) case object Int16 extends PrimitiveType(Data.Type.Int16) case object Int32 extends PrimitiveType(Data.Type.Int32) + case object Int64 extends PrimitiveType(Data.Type.Int64) case object BigInt extends PrimitiveType(Data.Type.BigInt) - case object Uint8 extends PrimitiveType(Data.Type.Uint8) - case object Uint16 extends PrimitiveType(Data.Type.Uint16) - case object Uint32 extends PrimitiveType(Data.Type.Uint32) case object Number extends PrimitiveType(Data.Type.Number) case object Boolean extends PrimitiveType(Data.Type.Boolean) case object Ref extends PrimitiveType(Data.Type.Ref) @@ -125,10 +121,8 @@ object Meta { case Data.Type.Int8 => Some(Int8) case Data.Type.Int16 => Some(Int16) case Data.Type.Int32 => Some(Int32) + case Data.Type.Int64 => Some(Int64) case Data.Type.BigInt => Some(BigInt) - case Data.Type.Uint8 => Some(Uint8) - case Data.Type.Uint16 => Some(Uint16) - case Data.Type.Uint32 => Some(Uint32) case Data.Type.Number => Some(Number) case Data.Type.Boolean => Some(Boolean) case Data.Type.Ref => Some(Ref) diff --git a/vm-api/src/main/scala/pravda/vm/Opcodes.scala b/vm-api/src/main/scala/pravda/vm/Opcodes.scala index cea22180..5b9afd33 100644 --- a/vm-api/src/main/scala/pravda/vm/Opcodes.scala +++ b/vm-api/src/main/scala/pravda/vm/Opcodes.scala @@ -86,6 +86,9 @@ object Opcodes { final val SEAL = 0xa8 final val THROW = 0xa9 final val EVENT = 0xaa + final val CALLERS = 0xab + final val HEIGHT = 0xac + final val HASH = 0xad // Native coins final val TRANSFER = 0xc0 diff --git a/vm-api/src/main/scala/pravda/vm/TethysInstances.scala b/vm-api/src/main/scala/pravda/vm/TethysInstances.scala index 29c731dc..62943d10 100644 --- a/vm-api/src/main/scala/pravda/vm/TethysInstances.scala +++ b/vm-api/src/main/scala/pravda/vm/TethysInstances.scala @@ -56,9 +56,7 @@ trait TethysInstances { case "int8" => Data.Primitive.Int8(v.toByte) case "int16" => Data.Primitive.Int16(v.toShort) case "int32" => Data.Primitive.Int32(v.toInt) - case "uint8" => Data.Primitive.Uint8(v.toInt) - case "uint16" => Data.Primitive.Uint16(v.toInt) - case "uint32" => Data.Primitive.Uint32(v.toLong) + case "int64" => Data.Primitive.Int64(v.toLong) case "bigint" => Data.Primitive.BigInt(BigInt(v)) case "number" => Data.Primitive.Number(v.toDouble) case "ref" => Data.Primitive.Ref(v.toInt) @@ -89,9 +87,7 @@ trait TethysInstances { case "int8" => Data.Array.Int8Array(readArray(_.string().toByte)) case "int16" => Data.Array.Int16Array(readArray(_.string().toShort)) case "int32" => Data.Array.Int32Array(readArray(_.string().toInt)) - case "uint8" => Data.Array.Uint8Array(readArray(_.string().toInt)) - case "uint16" => Data.Array.Uint16Array(readArray(_.string().toInt)) - case "uint32" => Data.Array.Uint32Array(readArray(_.string().toLong)) + case "int64" => Data.Array.Int64Array(readArray(_.string().toLong)) case "bigint" => Data.Array.BigIntArray(readArray(x => BigInt(x.string()))) case "number" => Data.Array.NumberArray(readArray(_.string().toDouble)) case "ref" => Data.Array.RefArray(readArray(_.string().toInt)) @@ -124,9 +120,7 @@ trait TethysInstances { case Data.Primitive.Int8(x) => f(s"int8.$x") case Data.Primitive.Int16(x) => f(s"int16.$x") case Data.Primitive.Int32(x) => f(s"int32.$x") - case Data.Primitive.Uint8(x) => f(s"uint8.$x") - case Data.Primitive.Uint16(x) => f(s"uint16.$x") - case Data.Primitive.Uint32(x) => f(s"uint32.$x") + case Data.Primitive.Int64(x) => f(s"int64.$x") case Data.Primitive.Number(x) => f(s"number.$x") case Data.Primitive.BigInt(x) => f(s"bigint.$x") case Data.Primitive.Ref(x) => f(s"ref.$x") @@ -154,9 +148,7 @@ trait TethysInstances { case Data.Array.Int8Array(xs) => writeArray(xs, "int8")(x => w.writeString(x.toString)) case Data.Array.Int16Array(xs) => writeArray(xs, "int16")(x => w.writeString(x.toString)) case Data.Array.Int32Array(xs) => writeArray(xs, "int32")(x => w.writeString(x.toString)) - case Data.Array.Uint8Array(xs) => writeArray(xs, "uint8")(x => w.writeString(x.toString)) - case Data.Array.Uint16Array(xs) => writeArray(xs, "uint16")(x => w.writeString(x.toString)) - case Data.Array.Uint32Array(xs) => writeArray(xs, "uint32")(x => w.writeString(x.toString)) + case Data.Array.Int64Array(xs) => writeArray(xs, "int64")(x => w.writeString(x.toString)) case Data.Array.NumberArray(xs) => writeArray(xs, "number")(x => w.writeString(x.toString)) case Data.Array.BigIntArray(xs) => writeArray(xs, "bigint")(x => w.writeString(x.toString)) case Data.Array.RefArray(xs) => writeArray(xs, "ref")(x => w.writeString(x.toString)) @@ -216,11 +208,11 @@ trait TethysInstances { implicit val primitiveBytesWriter: JsonWriter[Data.Primitive.Bytes] = JsonWriter.stringWriter.contramap(s => s"bytes.${bytes.byteString2hex(s.data)}") - implicit val primitiveBigIntReader: JsonReader[Data.Primitive.BigInt] = - JsonReader.stringReader.map(s => Data.Primitive.BigInt(BigInt(s.stripPrefix("bigint.")))) + implicit val primitiveInt64Reader: JsonReader[Data.Primitive.Int64] = + JsonReader.stringReader.map(s => Data.Primitive.Int64(s.stripPrefix("int64.").toLong)) - implicit val primitiveBigIntWriter: JsonWriter[Data.Primitive.BigInt] = - JsonWriter.stringWriter.contramap(b => s"bigint.$b") + implicit val primitiveInt64Writer: JsonWriter[Data.Primitive.Int64] = + JsonWriter.stringWriter.contramap(b => s"int64.$b") implicit val primitiveRefSupport: MapKeySupport[Data.Primitive.Ref] = new MapKeySupport[Data.Primitive.Ref] { def show(s: Data.Primitive.Ref): String = s"ref:${s.data}" diff --git a/vm-api/src/test/scala/pravda/vm/DataSpecification.scala b/vm-api/src/test/scala/pravda/vm/DataSpecification.scala index 7429e1f8..d3ad86df 100644 --- a/vm-api/src/test/scala/pravda/vm/DataSpecification.scala +++ b/vm-api/src/test/scala/pravda/vm/DataSpecification.scala @@ -24,17 +24,14 @@ import scala.collection.mutable val (int16, int16Array) = genPrimitive(Gen.chooseNum[Short](Short.MinValue, (Byte.MinValue - 1).toShort), Int16, Int16Array) val (int32, int32Array) = genPrimitive(Gen.chooseNum[Int](Int.MinValue, Short.MinValue - 1), Int32, Int32Array) - - val (uint8, uint8Array) = genPrimitive(Gen.chooseNum(0, 0xFF), Uint8, Uint8Array) - val (uint16, uint16Array) = genPrimitive(Gen.chooseNum(0xFF + 1, 0xFFFF), Uint16, Uint16Array) - val (uint32, uint32Array) = genPrimitive(Gen.chooseNum(0xFFFFl + 1, 0xFFFFFFFFl), Uint32, Uint32Array) + val (int64, int64Array) = genPrimitive(Gen.chooseNum[Long](Long.MinValue, Int.MinValue - 1L), Int64, Int64Array) val (bigInt, bigIntArray) = { - //val n = arbitrary[scala.BigInt].suchThat(x => x < Int.MinValue && x > 0xFFFFFFFFl) - val i = Gen.chooseNum[Int](1, Int.MaxValue) + //val n = arbitrary[scala.BigInt].suchThat(x => x < Long.MinValue && x > Long.MaxValue) + val i = Gen.chooseNum[Long](1, Long.MaxValue) val n = Gen.oneOf( - i.map(x => scala.BigInt(Int.MinValue) - x), - i.map(x => scala.BigInt(0xFFFFFFFFl) + x) + i.map(x => scala.BigInt(Long.MinValue) - x), + i.map(x => scala.BigInt(Long.MaxValue) + x) ) genPrimitive(n, BigInt, BigIntArray) } @@ -70,10 +67,8 @@ import scala.collection.mutable int8, int16, int32, + int64, utf8, - uint8, - uint16, - uint32, bigInt, number, bytes, @@ -94,9 +89,7 @@ import scala.collection.mutable int8Array, int16Array, int32Array, - uint8Array, - uint16Array, - uint32Array, + int64Array, bigIntArray, numberArray, bytesArray, @@ -116,9 +109,7 @@ import scala.collection.mutable toInt8: Boolean = true, toInt16: Boolean = true, toInt32: Boolean = true, - toUint8: Boolean = true, - toUint16: Boolean = true, - toUint32: Boolean = true, + toInt64: Boolean = true, toBigInt: Boolean = true, toNumber: Boolean = true, toRef: Boolean = true, @@ -127,10 +118,8 @@ import scala.collection.mutable (!toInt8 || data.cast(Type.Int8).cast(`type`) == data) :| "to int8" && (!toInt16 || data.cast(Type.Int16).cast(`type`) == data) :| "to int16" && (!toInt32 || data.cast(Type.Int32).cast(`type`) == data) :| "to int32" && - (!toUint8 || data.cast(Type.Uint8).cast(`type`) == data) :| "to uint8" && - (!toUint16 || data.cast(Type.Uint16).cast(`type`) == data) :| "to uint16" && - (!toUint32 || data.cast(Type.Uint32).cast(`type`) == data) :| "to uint32" && - (!toBigInt || data.cast(Type.BigInt).cast(`type`) == data) :| "to uint32" && + (!toInt64 || data.cast(Type.Int64).cast(`type`) == data) :| "to int64" && + (!toBigInt || data.cast(Type.BigInt).cast(`type`) == data) :| "to bigint" && (!toNumber || data.cast(Type.Number).cast(`type`) == data) :| "to number" && (!toRef || data.cast(Type.Ref).cast(`type`) == data) :| "to ref" && (!toUtf8 || data.cast(Type.Utf8).cast(`type`) == data) :| "to utf8" && @@ -138,13 +127,10 @@ import scala.collection.mutable } property("int8.cast") = castProperty(int8, Type.Int8) - property("int16.cast") = castProperty(int16, Type.Int16, toInt8 = false, toUint8 = false) - property("int32.cast") = - castProperty(int32, Type.Int32, toInt8 = false, toUint8 = false, toInt16 = false, toUint16 = false) - property("uint8.cast") = castProperty(uint8, Type.Uint8) - property("uint16.cast") = castProperty(uint16, Type.Uint16, toInt8 = false, toUint8 = false) - property("uint32.cast") = - castProperty(uint32, Type.Uint32, toInt8 = false, toUint8 = false, toInt16 = false, toUint16 = false) + property("int16.cast") = castProperty(int16, Type.Int16, toInt8 = false) + property("int32.cast") = castProperty(int32, Type.Int32, toInt8 = false, toInt16 = false) + property("int64.cast") = + castProperty(int64, Type.Int64, toInt8 = false, toInt16 = false, toInt32 = false, toRef = false, toNumber = false) property("utf8.cast") = castProperty(Gen.choose(0, 127).map(x => Utf8(x.toString)), Type.Utf8, toNumber = false) property("ref.cast") = castProperty(Gen.choose(0, 127).map(x => Ref(x)), Type.Ref) diff --git a/vm-api/src/test/scala/pravda/vm/MetaSpecification.scala b/vm-api/src/test/scala/pravda/vm/MetaSpecification.scala index dc1030b2..32a1d88e 100644 --- a/vm-api/src/test/scala/pravda/vm/MetaSpecification.scala +++ b/vm-api/src/test/scala/pravda/vm/MetaSpecification.scala @@ -16,9 +16,7 @@ import scala.annotation.strictfp TypeSignature.Int8, TypeSignature.Int16, TypeSignature.Int32, - TypeSignature.Uint8, - TypeSignature.Uint16, - TypeSignature.Uint32, + TypeSignature.Int64, TypeSignature.BigInt, TypeSignature.Number, TypeSignature.Boolean, diff --git a/vm-asm/src/main/scala/pravda/vm/asm/Operation.scala b/vm-asm/src/main/scala/pravda/vm/asm/Operation.scala index b9b0fa79..99401595 100644 --- a/vm-asm/src/main/scala/pravda/vm/asm/Operation.scala +++ b/vm-asm/src/main/scala/pravda/vm/asm/Operation.scala @@ -88,7 +88,10 @@ object Operation { Orphan(NEW_ARRAY), Orphan(LENGTH), Orphan(THROW), - Orphan(EVENT) + Orphan(EVENT), + Orphan(CALLERS), + Orphan(HEIGHT), + Orphan(HASH) ) val mnemonicByOpcode: Map[Int, String] = Map( @@ -144,7 +147,10 @@ object Operation { NEW_ARRAY -> "new_array", LENGTH -> "length", THROW -> "throw", - EVENT -> "event" + EVENT -> "event", + CALLERS -> "callers", + HEIGHT -> "height", + HASH -> "hash" ) /** diff --git a/vm/src/main/scala/pravda/vm/AppStateInfo.scala b/vm/src/main/scala/pravda/vm/AppStateInfo.scala new file mode 100644 index 00000000..c5d136ad --- /dev/null +++ b/vm/src/main/scala/pravda/vm/AppStateInfo.scala @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2018 Expload.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package pravda.vm + +import com.google.protobuf.ByteString + +case class AppStateInfo(`app-hash`: ByteString, height: Long) diff --git a/vm/src/main/scala/pravda/vm/Environment.scala b/vm/src/main/scala/pravda/vm/Environment.scala index 0fbc1f55..cd81d67d 100644 --- a/vm/src/main/scala/pravda/vm/Environment.scala +++ b/vm/src/main/scala/pravda/vm/Environment.scala @@ -27,6 +27,9 @@ trait Environment { */ def executor: Address + def chainHeight: Long + def lastBlockHash: ByteString + // Programs def sealProgram(address: Address): Unit def updateProgram(address: Address, code: ByteString): Unit diff --git a/vm/src/main/scala/pravda/vm/StandardLibrary.scala b/vm/src/main/scala/pravda/vm/StandardLibrary.scala index 433c4822..5b47599f 100644 --- a/vm/src/main/scala/pravda/vm/StandardLibrary.scala +++ b/vm/src/main/scala/pravda/vm/StandardLibrary.scala @@ -23,7 +23,12 @@ object StandardLibrary { standard.Ripemd160, standard.ValidateEd25519Signature, standard.ExponentialFunction, - standard.HexToBytes + standard.HexToBytes, + standard.BytesToHex, + standard.SliceByteArray, + standard.WriteSliceByteArray, + standard.ExpandBytesEvm, + standard.Sha3 ) val Index: Map[Long, (Memory, WattCounter) => Unit] = diff --git a/vm/src/main/scala/pravda/vm/impl/VmImpl.scala b/vm/src/main/scala/pravda/vm/impl/VmImpl.scala index c65efc57..9e0b19e8 100644 --- a/vm/src/main/scala/pravda/vm/impl/VmImpl.scala +++ b/vm/src/main/scala/pravda/vm/impl/VmImpl.scala @@ -177,11 +177,14 @@ class VmImpl extends Vm { } else { throw ThrowableVmError(PcallDenied) } - case THROW => systemOperations.`throw`() - case EVENT => systemOperations.event() - case CODE => systemOperations.code() - case META => Meta.readFromByteBuffer(program) - case _ => + case THROW => systemOperations.`throw`() + case EVENT => systemOperations.event() + case CALLERS => systemOperations.callers() + case HEIGHT => systemOperations.chainHeight() + case HASH => systemOperations.lastBlockHash() + case CODE => systemOperations.code() + case META => Meta.readFromByteBuffer(program) + case _ => } } } diff --git a/vm/src/main/scala/pravda/vm/operations/ArithmeticOperations.scala b/vm/src/main/scala/pravda/vm/operations/ArithmeticOperations.scala index ea469420..1ad560b3 100644 --- a/vm/src/main/scala/pravda/vm/operations/ArithmeticOperations.scala +++ b/vm/src/main/scala/pravda/vm/operations/ArithmeticOperations.scala @@ -17,7 +17,7 @@ package pravda.vm.operations -import pravda.vm.Data.Primitive.{BigInt, Int16, Int32, Int8, Number, Uint16, Uint32, Uint8} +import pravda.vm.Data.Primitive.{BigInt, Int16, Int32, Int64, Int8, Number} import pravda.vm.Error.WrongType import pravda.vm.WattCounter.CpuArithmetic import pravda.vm.operations.annotation.OpcodeImplementation @@ -25,6 +25,7 @@ import pravda.vm.{Memory, Opcodes, ThrowableVmError, WattCounter} /** * Pravda VM arithmetic opcodes implementation. + * * @param memory Access to VM memory * @param wattCounter CPU, memory, storage usage counter */ @@ -38,86 +39,52 @@ final class ArithmeticOperations(memory: Memory, wattCounter: WattCounter) { wattCounter.cpuUsage(CpuArithmetic) binaryOperation(memory, wattCounter) { (a, b) => a match { + case Int64(lhs) => + b match { + case Int8(rhs) => Int64(lhs % rhs) + case Int16(rhs) => Int64(lhs % rhs) + case Int32(rhs) => Int64(lhs % rhs) + case Int64(rhs) => Int64(lhs % rhs) + case BigInt(rhs) => BigInt(lhs % rhs) + case Number(rhs) => Number(lhs % rhs) + case _ => throw ThrowableVmError(WrongType) + } case Int32(lhs) => b match { case Int8(rhs) => Int32(lhs % rhs) case Int16(rhs) => Int32(lhs % rhs) case Int32(rhs) => Int32(lhs % rhs) - case Uint8(rhs) => Int32(lhs % rhs) - case Uint16(rhs) => Int32(lhs % rhs) - case Uint32(rhs) => Int32((lhs % rhs).toInt) - case BigInt(rhs) => Int32((lhs % rhs).toInt) - case Number(rhs) => Int32((lhs % rhs).toInt) + case Int64(rhs) => Int64(lhs % rhs) + case BigInt(rhs) => BigInt(lhs % rhs) + case Number(rhs) => Number(lhs % rhs) case _ => throw ThrowableVmError(WrongType) } case Int16(lhs) => b match { case Int8(rhs) => Int16((lhs % rhs).toShort) case Int16(rhs) => Int16((lhs % rhs).toShort) - case Int32(rhs) => Int16((lhs % rhs).toShort) - case Uint8(rhs) => Int16((lhs % rhs).toShort) - case Uint16(rhs) => Int16((lhs % rhs).toShort) - case Uint32(rhs) => Int16((lhs % rhs).toShort) - case BigInt(rhs) => Int16((lhs.toInt % rhs).toShort) - case Number(rhs) => Int16((lhs % rhs).toShort) + case Int32(rhs) => Int32(lhs % rhs) + case Int64(rhs) => Int64(lhs % rhs) + case BigInt(rhs) => BigInt(lhs.toInt % rhs) + case Number(rhs) => Number(lhs % rhs) case _ => throw ThrowableVmError(WrongType) } case Int8(lhs) => b match { - case Int8(rhs) => Int32(lhs % rhs) - case Int16(rhs) => Int32(lhs % rhs) + case Int8(rhs) => Int8((lhs % rhs).toByte) + case Int16(rhs) => Int16((lhs % rhs).toShort) case Int32(rhs) => Int32(lhs % rhs) - case Uint8(rhs) => Int32(lhs % rhs) - case Uint16(rhs) => Int32(lhs % rhs) - case Uint32(rhs) => BigInt(lhs % rhs) + case Int64(rhs) => Int64(lhs % rhs) case BigInt(rhs) => BigInt(lhs.toInt % rhs) case Number(rhs) => Number(lhs % rhs) case _ => throw ThrowableVmError(WrongType) } - case Uint8(lhs) => - b match { - case Int8(rhs) => Uint8(lhs % rhs) - case Int16(rhs) => Uint8(lhs % rhs) - case Int32(rhs) => Uint8(lhs % rhs) - case Uint8(rhs) => Uint8(lhs % rhs) - case Uint16(rhs) => Uint8(lhs % rhs) - case Uint32(rhs) => Uint8((lhs % rhs).toInt) - case BigInt(rhs) => Uint8((lhs % rhs).toInt) - case Number(rhs) => Uint8((lhs % rhs).toInt) - case _ => throw ThrowableVmError(WrongType) - } - case Uint16(lhs) => - b match { - case Int8(rhs) => Uint16(lhs % rhs) - case Int16(rhs) => Uint16(lhs % rhs) - case Int32(rhs) => Uint16(lhs % rhs) - case Uint8(rhs) => Uint16(lhs % rhs) - case Uint16(rhs) => Uint16(lhs % rhs) - case Uint32(rhs) => Uint16((lhs % rhs).toInt) - case BigInt(rhs) => Uint16((lhs % rhs).toInt) - case Number(rhs) => Uint16((lhs % rhs).toInt) - case _ => throw ThrowableVmError(WrongType) - } - case Uint32(lhs) => - b match { - case Int8(rhs) => Uint32(lhs % rhs) - case Int16(rhs) => Uint32(lhs % rhs) - case Int32(rhs) => Uint32(lhs % rhs) - case Uint8(rhs) => Uint32(lhs % rhs) - case Uint16(rhs) => Uint32(lhs % rhs) - case Uint32(rhs) => Uint32(lhs % rhs) - case BigInt(rhs) => Uint32((lhs % rhs).toLong) - case Number(rhs) => Uint32((lhs % rhs).toLong) - case _ => throw ThrowableVmError(WrongType) - } case Number(lhs) => b match { case Int8(rhs) => Number(lhs % rhs) case Int16(rhs) => Number(lhs % rhs) case Int32(rhs) => Number(lhs % rhs) - case Uint8(rhs) => Number(lhs % rhs) - case Uint16(rhs) => Number(lhs % rhs) - case Uint32(rhs) => Number(lhs % rhs) + case Int64(rhs) => Number(lhs % rhs) case BigInt(rhs) => Number((lhs % BigDecimal(rhs)).toDouble) case Number(rhs) => Number(lhs % rhs) case _ => throw ThrowableVmError(WrongType) @@ -127,11 +94,9 @@ final class ArithmeticOperations(memory: Memory, wattCounter: WattCounter) { case Int8(rhs) => BigInt(lhs % scala.BigInt(rhs.toInt)) case Int16(rhs) => BigInt(lhs % scala.BigInt(rhs.toInt)) case Int32(rhs) => BigInt(lhs % rhs) - case Uint8(rhs) => BigInt(lhs % rhs) - case Uint16(rhs) => BigInt(lhs % rhs) - case Uint32(rhs) => BigInt(lhs % rhs) + case Int64(rhs) => BigInt(lhs % rhs) case BigInt(rhs) => BigInt(lhs % rhs) - case Number(rhs) => BigInt(lhs % rhs.toLong) + case Number(rhs) => Number(lhs.toDouble % rhs) case _ => throw ThrowableVmError(WrongType) } case _ => throw ThrowableVmError(WrongType) @@ -147,86 +112,52 @@ final class ArithmeticOperations(memory: Memory, wattCounter: WattCounter) { wattCounter.cpuUsage(CpuArithmetic) binaryOperation(memory, wattCounter) { (a, b) => a match { + case Int64(lhs) => + b match { + case Int8(rhs) => Int64(lhs + rhs) + case Int16(rhs) => Int64(lhs + rhs) + case Int32(rhs) => Int64(lhs + rhs) + case Int64(rhs) => Int64(lhs + rhs) + case BigInt(rhs) => BigInt(lhs + rhs) + case Number(rhs) => Number(lhs + rhs) + case _ => throw ThrowableVmError(WrongType) + } case Int32(lhs) => b match { case Int8(rhs) => Int32(lhs + rhs) case Int16(rhs) => Int32(lhs + rhs) case Int32(rhs) => Int32(lhs + rhs) - case Uint8(rhs) => Int32(lhs + rhs) - case Uint16(rhs) => Int32(lhs + rhs) - case Uint32(rhs) => Int32((lhs + rhs).toInt) - case BigInt(rhs) => Int32((lhs + rhs).toInt) - case Number(rhs) => Int32((lhs + rhs).toInt) + case Int64(rhs) => Int64(lhs + rhs) + case BigInt(rhs) => BigInt(lhs + rhs) + case Number(rhs) => Number(lhs + rhs) case _ => throw ThrowableVmError(WrongType) } case Int16(lhs) => b match { case Int8(rhs) => Int16((lhs + rhs).toShort) case Int16(rhs) => Int16((lhs + rhs).toShort) - case Int32(rhs) => Int16((lhs + rhs).toShort) - case Uint8(rhs) => Int16((lhs + rhs).toShort) - case Uint16(rhs) => Int16((lhs + rhs).toShort) - case Uint32(rhs) => Int16((lhs + rhs).toShort) - case BigInt(rhs) => Int16((lhs.toInt + rhs).toShort) - case Number(rhs) => Int16((lhs + rhs).toShort) + case Int32(rhs) => Int32(lhs + rhs) + case Int64(rhs) => Int64(lhs + rhs) + case BigInt(rhs) => BigInt(lhs.toInt + rhs) + case Number(rhs) => Number(lhs + rhs) case _ => throw ThrowableVmError(WrongType) } case Int8(lhs) => b match { - case Int8(rhs) => Int32(lhs + rhs) - case Int16(rhs) => Int32(lhs + rhs) + case Int8(rhs) => Int8((lhs + rhs).toByte) + case Int16(rhs) => Int16((lhs + rhs).toShort) case Int32(rhs) => Int32(lhs + rhs) - case Uint8(rhs) => Int32(lhs + rhs) - case Uint16(rhs) => Int32(lhs + rhs) - case Uint32(rhs) => BigInt(lhs + rhs) + case Int64(rhs) => Int64(lhs + rhs) case BigInt(rhs) => BigInt(lhs.toInt + rhs) case Number(rhs) => Number(lhs + rhs) case _ => throw ThrowableVmError(WrongType) } - case Uint8(lhs) => - b match { - case Int8(rhs) => Uint8(lhs + rhs) - case Int16(rhs) => Uint8(lhs + rhs) - case Int32(rhs) => Uint8(lhs + rhs) - case Uint8(rhs) => Uint8(lhs + rhs) - case Uint16(rhs) => Uint8(lhs + rhs) - case Uint32(rhs) => Uint8((lhs + rhs).toInt) - case BigInt(rhs) => Uint8((lhs + rhs).toInt) - case Number(rhs) => Uint8((lhs + rhs).toInt) - case _ => throw ThrowableVmError(WrongType) - } - case Uint16(lhs) => - b match { - case Int8(rhs) => Uint16(lhs + rhs) - case Int16(rhs) => Uint16(lhs + rhs) - case Int32(rhs) => Uint16(lhs + rhs) - case Uint8(rhs) => Uint16(lhs + rhs) - case Uint16(rhs) => Uint16(lhs + rhs) - case Uint32(rhs) => Uint16((lhs + rhs).toInt) - case BigInt(rhs) => Uint16((lhs + rhs).toInt) - case Number(rhs) => Uint16((lhs + rhs).toInt) - case _ => throw ThrowableVmError(WrongType) - } - case Uint32(lhs) => - b match { - case Int8(rhs) => Uint32(lhs + rhs) - case Int16(rhs) => Uint32(lhs + rhs) - case Int32(rhs) => Uint32(lhs + rhs) - case Uint8(rhs) => Uint32(lhs + rhs) - case Uint16(rhs) => Uint32(lhs + rhs) - case Uint32(rhs) => Uint32(lhs + rhs) - case BigInt(rhs) => Uint32((lhs + rhs).toLong) - case Number(rhs) => Uint32((lhs + rhs).toLong) - case _ => throw ThrowableVmError(WrongType) - } case Number(lhs) => b match { case Int8(rhs) => Number(lhs + rhs) case Int16(rhs) => Number(lhs + rhs) case Int32(rhs) => Number(lhs + rhs) - case Uint8(rhs) => Number(lhs + rhs) - case Uint16(rhs) => Number(lhs + rhs) - case Uint32(rhs) => Number(lhs + rhs) + case Int64(rhs) => Number(lhs + rhs) case BigInt(rhs) => Number((lhs + BigDecimal(rhs)).toDouble) case Number(rhs) => Number(lhs + rhs) case _ => throw ThrowableVmError(WrongType) @@ -236,11 +167,9 @@ final class ArithmeticOperations(memory: Memory, wattCounter: WattCounter) { case Int8(rhs) => BigInt(lhs + scala.BigInt(rhs.toInt)) case Int16(rhs) => BigInt(lhs + scala.BigInt(rhs.toInt)) case Int32(rhs) => BigInt(lhs + rhs) - case Uint8(rhs) => BigInt(lhs + rhs) - case Uint16(rhs) => BigInt(lhs + rhs) - case Uint32(rhs) => BigInt(lhs + rhs) + case Int64(rhs) => BigInt(lhs + rhs) case BigInt(rhs) => BigInt(lhs + rhs) - case Number(rhs) => BigInt(lhs + rhs.toLong) + case Number(rhs) => Number(lhs.toDouble + rhs) case _ => throw ThrowableVmError(WrongType) } case _ => throw ThrowableVmError(WrongType) @@ -256,86 +185,52 @@ final class ArithmeticOperations(memory: Memory, wattCounter: WattCounter) { wattCounter.cpuUsage(CpuArithmetic) binaryOperation(memory, wattCounter) { (a, b) => a match { + case Int64(lhs) => + b match { + case Int8(rhs) => Int64(lhs / rhs) + case Int16(rhs) => Int64(lhs / rhs) + case Int32(rhs) => Int64(lhs / rhs) + case Int64(rhs) => Int64(lhs / rhs) + case BigInt(rhs) => BigInt(lhs / rhs) + case Number(rhs) => Number(lhs / rhs) + case _ => throw ThrowableVmError(WrongType) + } case Int32(lhs) => b match { case Int8(rhs) => Int32(lhs / rhs) case Int16(rhs) => Int32(lhs / rhs) case Int32(rhs) => Int32(lhs / rhs) - case Uint8(rhs) => Int32(lhs / rhs) - case Uint16(rhs) => Int32(lhs / rhs) - case Uint32(rhs) => Int32((lhs / rhs).toInt) - case BigInt(rhs) => Int32((lhs / rhs).toInt) - case Number(rhs) => Int32((lhs / rhs).toInt) + case Int64(rhs) => Int64(lhs / rhs) + case BigInt(rhs) => BigInt(lhs / rhs) + case Number(rhs) => Number(lhs / rhs) case _ => throw ThrowableVmError(WrongType) } case Int16(lhs) => b match { case Int8(rhs) => Int16((lhs / rhs).toShort) case Int16(rhs) => Int16((lhs / rhs).toShort) - case Int32(rhs) => Int16((lhs / rhs).toShort) - case Uint8(rhs) => Int16((lhs / rhs).toShort) - case Uint16(rhs) => Int16((lhs / rhs).toShort) - case Uint32(rhs) => Int16((lhs / rhs).toShort) - case BigInt(rhs) => Int16((lhs.toInt / rhs).toShort) - case Number(rhs) => Int16((lhs / rhs).toShort) + case Int32(rhs) => Int32(lhs / rhs) + case Int64(rhs) => Int64(lhs / rhs) + case BigInt(rhs) => BigInt(lhs.toInt / rhs) + case Number(rhs) => Number(lhs / rhs) case _ => throw ThrowableVmError(WrongType) } case Int8(lhs) => b match { - case Int8(rhs) => Int32(lhs / rhs) - case Int16(rhs) => Int32(lhs / rhs) + case Int8(rhs) => Int8((lhs / rhs).toByte) + case Int16(rhs) => Int16((lhs / rhs).toShort) case Int32(rhs) => Int32(lhs / rhs) - case Uint8(rhs) => Int32(lhs / rhs) - case Uint16(rhs) => Int32(lhs / rhs) - case Uint32(rhs) => BigInt(lhs / rhs) + case Int64(rhs) => Int64(lhs / rhs) case BigInt(rhs) => BigInt(lhs.toInt / rhs) case Number(rhs) => Number(lhs / rhs) case _ => throw ThrowableVmError(WrongType) } - case Uint8(lhs) => - b match { - case Int8(rhs) => Uint8(lhs / rhs) - case Int16(rhs) => Uint8(lhs / rhs) - case Int32(rhs) => Uint8(lhs / rhs) - case Uint8(rhs) => Uint8(lhs / rhs) - case Uint16(rhs) => Uint8(lhs / rhs) - case Uint32(rhs) => Uint8((lhs / rhs).toInt) - case BigInt(rhs) => Uint8((lhs / rhs).toInt) - case Number(rhs) => Uint8((lhs / rhs).toInt) - case _ => throw ThrowableVmError(WrongType) - } - case Uint16(lhs) => - b match { - case Int8(rhs) => Uint16(lhs / rhs) - case Int16(rhs) => Uint16(lhs / rhs) - case Int32(rhs) => Uint16(lhs / rhs) - case Uint8(rhs) => Uint16(lhs / rhs) - case Uint16(rhs) => Uint16(lhs / rhs) - case Uint32(rhs) => Uint16((lhs / rhs).toInt) - case BigInt(rhs) => Uint16((lhs / rhs).toInt) - case Number(rhs) => Uint16((lhs / rhs).toInt) - case _ => throw ThrowableVmError(WrongType) - } - case Uint32(lhs) => - b match { - case Int8(rhs) => Uint32(lhs / rhs) - case Int16(rhs) => Uint32(lhs / rhs) - case Int32(rhs) => Uint32(lhs / rhs) - case Uint8(rhs) => Uint32(lhs / rhs) - case Uint16(rhs) => Uint32(lhs / rhs) - case Uint32(rhs) => Uint32(lhs / rhs) - case BigInt(rhs) => Uint32((lhs / rhs).toLong) - case Number(rhs) => Uint32((lhs / rhs).toLong) - case _ => throw ThrowableVmError(WrongType) - } case Number(lhs) => b match { case Int8(rhs) => Number(lhs / rhs) case Int16(rhs) => Number(lhs / rhs) case Int32(rhs) => Number(lhs / rhs) - case Uint8(rhs) => Number(lhs / rhs) - case Uint16(rhs) => Number(lhs / rhs) - case Uint32(rhs) => Number(lhs / rhs) + case Int64(rhs) => Number(lhs / rhs) case BigInt(rhs) => Number((lhs / BigDecimal(rhs)).toDouble) case Number(rhs) => Number(lhs / rhs) case _ => throw ThrowableVmError(WrongType) @@ -345,11 +240,9 @@ final class ArithmeticOperations(memory: Memory, wattCounter: WattCounter) { case Int8(rhs) => BigInt(lhs / scala.BigInt(rhs.toInt)) case Int16(rhs) => BigInt(lhs / scala.BigInt(rhs.toInt)) case Int32(rhs) => BigInt(lhs / rhs) - case Uint8(rhs) => BigInt(lhs / rhs) - case Uint16(rhs) => BigInt(lhs / rhs) - case Uint32(rhs) => BigInt(lhs / rhs) + case Int64(rhs) => BigInt(lhs / rhs) case BigInt(rhs) => BigInt(lhs / rhs) - case Number(rhs) => BigInt(lhs / rhs.toLong) + case Number(rhs) => Number(lhs.toDouble / rhs) case _ => throw ThrowableVmError(WrongType) } case _ => throw ThrowableVmError(WrongType) @@ -370,81 +263,37 @@ final class ArithmeticOperations(memory: Memory, wattCounter: WattCounter) { case Int8(rhs) => Int32(lhs * rhs) case Int16(rhs) => Int32(lhs * rhs) case Int32(rhs) => Int32(lhs * rhs) - case Uint8(rhs) => Int32(lhs * rhs) - case Uint16(rhs) => Int32(lhs * rhs) - case Uint32(rhs) => Int32((lhs * rhs).toInt) - case BigInt(rhs) => Int32((lhs * rhs).toInt) - case Number(rhs) => Int32((lhs * rhs).toInt) + case Int64(rhs) => Int64(lhs * rhs) + case BigInt(rhs) => BigInt(lhs * rhs) + case Number(rhs) => Number(lhs * rhs) case _ => throw ThrowableVmError(WrongType) } case Int16(lhs) => b match { case Int8(rhs) => Int16((lhs * rhs).toShort) case Int16(rhs) => Int16((lhs * rhs).toShort) - case Int32(rhs) => Int16((lhs * rhs).toShort) - case Uint8(rhs) => Int16((lhs * rhs).toShort) - case Uint16(rhs) => Int16((lhs * rhs).toShort) - case Uint32(rhs) => Int16((lhs * rhs).toShort) - case BigInt(rhs) => Int16((lhs.toInt * rhs).toShort) - case Number(rhs) => Int16((lhs * rhs).toShort) + case Int32(rhs) => Int32(lhs * rhs) + case Int64(rhs) => Int64(lhs * rhs) + case BigInt(rhs) => BigInt(lhs.toInt * rhs) + case Number(rhs) => Number(lhs * rhs) case _ => throw ThrowableVmError(WrongType) } case Int8(lhs) => b match { - case Int8(rhs) => Int32(lhs * rhs) - case Int16(rhs) => Int32(lhs * rhs) + case Int8(rhs) => Int8((lhs * rhs).toByte) + case Int16(rhs) => Int16((lhs * rhs).toShort) case Int32(rhs) => Int32(lhs * rhs) - case Uint8(rhs) => Int32(lhs * rhs) - case Uint16(rhs) => Int32(lhs * rhs) - case Uint32(rhs) => BigInt(lhs * rhs) + case Int64(rhs) => Int64(lhs * rhs) case BigInt(rhs) => BigInt(lhs.toInt * rhs) case Number(rhs) => Number(lhs * rhs) case _ => throw ThrowableVmError(WrongType) } - case Uint8(lhs) => - b match { - case Int8(rhs) => Uint8(lhs * rhs) - case Int16(rhs) => Uint8(lhs * rhs) - case Int32(rhs) => Uint8(lhs * rhs) - case Uint8(rhs) => Uint8(lhs * rhs) - case Uint16(rhs) => Uint8(lhs * rhs) - case Uint32(rhs) => Uint8((lhs * rhs).toInt) - case BigInt(rhs) => Uint8((lhs * rhs).toInt) - case Number(rhs) => Uint8((lhs * rhs).toInt) - case _ => throw ThrowableVmError(WrongType) - } - case Uint16(lhs) => - b match { - case Int8(rhs) => Uint16(lhs * rhs) - case Int16(rhs) => Uint16(lhs * rhs) - case Int32(rhs) => Uint16(lhs * rhs) - case Uint8(rhs) => Uint16(lhs * rhs) - case Uint16(rhs) => Uint16(lhs * rhs) - case Uint32(rhs) => Uint16((lhs * rhs).toInt) - case BigInt(rhs) => Uint16((lhs * rhs).toInt) - case Number(rhs) => Uint16((lhs * rhs).toInt) - case _ => throw ThrowableVmError(WrongType) - } - case Uint32(lhs) => - b match { - case Int8(rhs) => Uint32(lhs * rhs) - case Int16(rhs) => Uint32(lhs * rhs) - case Int32(rhs) => Uint32(lhs * rhs) - case Uint8(rhs) => Uint32(lhs * rhs) - case Uint16(rhs) => Uint32(lhs * rhs) - case Uint32(rhs) => Uint32(lhs * rhs) - case BigInt(rhs) => Uint32((lhs * rhs).toLong) - case Number(rhs) => Uint32((lhs * rhs).toLong) - case _ => throw ThrowableVmError(WrongType) - } case Number(lhs) => b match { case Int8(rhs) => Number(lhs * rhs) case Int16(rhs) => Number(lhs * rhs) case Int32(rhs) => Number(lhs * rhs) - case Uint8(rhs) => Number(lhs * rhs) - case Uint16(rhs) => Number(lhs * rhs) - case Uint32(rhs) => Number(lhs * rhs) + case Int64(rhs) => Number(lhs * rhs) case BigInt(rhs) => Number((lhs * BigDecimal(rhs)).toDouble) case Number(rhs) => Number(lhs * rhs) case _ => throw ThrowableVmError(WrongType) @@ -454,11 +303,9 @@ final class ArithmeticOperations(memory: Memory, wattCounter: WattCounter) { case Int8(rhs) => BigInt(lhs * scala.BigInt(rhs.toInt)) case Int16(rhs) => BigInt(lhs * scala.BigInt(rhs.toInt)) case Int32(rhs) => BigInt(lhs * rhs) - case Uint8(rhs) => BigInt(lhs * rhs) - case Uint16(rhs) => BigInt(lhs * rhs) - case Uint32(rhs) => BigInt(lhs * rhs) + case Int64(rhs) => BigInt(lhs * rhs) case BigInt(rhs) => BigInt(lhs * rhs) - case Number(rhs) => BigInt(lhs * rhs.toLong) + case Number(rhs) => Number(lhs.toDouble * rhs) case _ => throw ThrowableVmError(WrongType) } case _ => throw ThrowableVmError(WrongType) diff --git a/vm/src/main/scala/pravda/vm/operations/HeapOperations.scala b/vm/src/main/scala/pravda/vm/operations/HeapOperations.scala index 8562e5ce..73b9a9f2 100644 --- a/vm/src/main/scala/pravda/vm/operations/HeapOperations.scala +++ b/vm/src/main/scala/pravda/vm/operations/HeapOperations.scala @@ -24,7 +24,7 @@ import pravda.vm.Data.Array._ import pravda.vm.Data.Primitive.{Bool, _} import pravda.vm.Data.{Primitive, Struct, Type} import pravda.vm.Opcodes._ -import pravda.vm.Error.WrongType +import pravda.vm.Error.{InvalidArgument, WrongType} import pravda.vm._ import pravda.vm.operations.annotation.OpcodeImplementation @@ -104,9 +104,7 @@ final class HeapOperations(memory: Memory, program: ByteBuffer, wattCounter: Wat case Type.Int8 => Data.Array.Int8Array(ArrayBuffer.fill(num.toInt)(0)) case Type.Int16 => Data.Array.Int16Array(ArrayBuffer.fill(num.toInt)(0)) case Type.Int32 => Data.Array.Int32Array(ArrayBuffer.fill(num.toInt)(0)) - case Type.Uint8 => Data.Array.Uint8Array(ArrayBuffer.fill(num.toInt)(0)) - case Type.Uint16 => Data.Array.Uint16Array(ArrayBuffer.fill(num.toInt)(0)) - case Type.Uint32 => Data.Array.Uint32Array(ArrayBuffer.fill(num.toInt)(0L)) + case Type.Int64 => Data.Array.Int64Array(ArrayBuffer.fill(num.toInt)(0L)) case Type.BigInt => Data.Array.BigIntArray(ArrayBuffer.fill(num.toInt)(scala.BigInt(0))) case Type.Number => Data.Array.NumberArray(ArrayBuffer.fill(num.toInt)(0.0)) case Type.Ref => Data.Array.RefArray(ArrayBuffer.fill(num.toInt)(0)) @@ -126,32 +124,38 @@ final class HeapOperations(memory: Memory, program: ByteBuffer, wattCounter: Wat ) def arrayGet(): Unit = { val index = integer(memory.pop()).toInt + + def readArray[T](read: Int => T, len: Int): T = + if (index < 0 || index >= len) { + throw ThrowableVmError(InvalidArgument) + } else { + read(index) + } + memory.pop() match { case reference: Ref => val datum = memory.heapGet(reference) match { - case Bytes(data) => Uint8(data.byteAt(index) & 0xFF) - case Utf8(data) => Uint8(data.charAt(index).toByte & 0xFF) - case Int8Array(data) => Int8(data(index)) - case Int16Array(data) => Int16(data(index)) - case Int32Array(data) => Int32(data(index)) - case Uint8Array(data) => Uint8(data(index)) - case Uint16Array(data) => Uint16(data(index)) - case Uint32Array(data) => Uint32(data(index)) - case BigIntArray(data) => BigInt(data(index)) - case RefArray(data) => Ref(data(index)) - case BoolArray(data) => data(index) - case Utf8Array(data) => Utf8(data(index)) - case BytesArray(data) => Bytes(data(index)) + case Bytes(data) => Int8(readArray(data.byteAt, data.size())) + case Utf8(data) => Int8(readArray(data.charAt, data.length).toByte) + case Int8Array(data) => Int8(readArray(data.apply, data.length)) + case Int16Array(data) => Int16(readArray(data.apply, data.length)) + case Int32Array(data) => Int32(readArray(data.apply, data.length)) + case Int64Array(data) => Int64(readArray(data.apply, data.length)) + case BigIntArray(data) => BigInt(readArray(data.apply, data.length)) + case RefArray(data) => Ref(readArray(data.apply, data.length)) + case BoolArray(data) => readArray(data.apply, data.length) + case Utf8Array(data) => Utf8(readArray(data.apply, data.length)) + case BytesArray(data) => Bytes(readArray(data.apply, data.length)) case _ => throw ThrowableVmError(WrongType) } wattCounter.memoryUsage(datum.volume.toLong) memory.push(datum) case Utf8(data) => - val datum = Uint8(data.charAt(index).toByte & 0xFF) + val datum = Int8(readArray(data.charAt, data.length).toByte) wattCounter.memoryUsage(datum.volume.toLong) memory.push(datum) case Bytes(data) => - val datum = Uint8(data.byteAt(index) & 0xFF) + val datum = Int8(readArray(data.byteAt, data.size())) wattCounter.memoryUsage(datum.volume.toLong) memory.push(datum) case _ => throw ThrowableVmError(WrongType) @@ -168,18 +172,24 @@ final class HeapOperations(memory: Memory, program: ByteBuffer, wattCounter: Wat val primitive = memory.pop() val reference = ref(memory.pop()) val array = memory.heapGet(reference) + + def writeArray[T](value: T, write: (Int, T) => Unit, len: Int): Unit = + if (index < 0 || index >= len) { + throw ThrowableVmError(InvalidArgument) + } else { + write(index, value) + } + (primitive, array) match { - case (Int8(value), Int8Array(data)) => data(index) = value - case (Int16(value), Int16Array(data)) => data(index) = value - case (Int32(value), Int32Array(data)) => data(index) = value - case (Uint8(value), Uint8Array(data)) => data(index) = value - case (Uint16(value), Uint16Array(data)) => data(index) = value - case (Uint32(value), Uint32Array(data)) => data(index) = value - case (BigInt(value), BigIntArray(data)) => data(index) = value - case (Ref(value), RefArray(data)) => data(index) = value - case (value: Bool, BoolArray(data)) => data(index) = value - case (Utf8(value), Utf8Array(data)) => data(index) = value - case (Bytes(value), BytesArray(data)) => data(index) = value + case (Int8(value), Int8Array(data)) => writeArray(value, data.update, data.length) + case (Int16(value), Int16Array(data)) => writeArray(value, data.update, data.length) + case (Int32(value), Int32Array(data)) => writeArray(value, data.update, data.length) + case (Int64(value), Int64Array(data)) => writeArray(value, data.update, data.length) + case (BigInt(value), BigIntArray(data)) => writeArray(value, data.update, data.length) + case (Ref(value), RefArray(data)) => writeArray(value, data.update, data.length) + case (value: Bool, BoolArray(data)) => writeArray(value, data.update, data.length) + case (Utf8(value), Utf8Array(data)) => writeArray(value, data.update, data.length) + case (Bytes(value), BytesArray(data)) => writeArray(value, data.update, data.length) case _ => throw ThrowableVmError(WrongType) } } @@ -197,9 +207,7 @@ final class HeapOperations(memory: Memory, program: ByteBuffer, wattCounter: Wat case Int8Array(data) => data.length case Int16Array(data) => data.length case Int32Array(data) => data.length - case Uint8Array(data) => data.length - case Uint16Array(data) => data.length - case Uint32Array(data) => data.length + case Int64Array(data) => data.length case BigIntArray(data) => data.length case RefArray(data) => data.length case BoolArray(data) => data.length @@ -212,7 +220,7 @@ final class HeapOperations(memory: Memory, program: ByteBuffer, wattCounter: Wat case _ => throw ThrowableVmError(WrongType) } - memory.push(Data.Primitive.Uint32(len.toLong)) + memory.push(Data.Primitive.Int32(len)) } @OpcodeImplementation( diff --git a/vm/src/main/scala/pravda/vm/operations/LogicalOperations.scala b/vm/src/main/scala/pravda/vm/operations/LogicalOperations.scala index d9beb6a9..a729d445 100644 --- a/vm/src/main/scala/pravda/vm/operations/LogicalOperations.scala +++ b/vm/src/main/scala/pravda/vm/operations/LogicalOperations.scala @@ -17,6 +17,7 @@ package pravda.vm.operations +import com.google.protobuf.ByteString import pravda.vm.Error.WrongType import pravda.vm.WattCounter.CpuSimpleArithmetic import pravda.vm.operations.annotation.OpcodeImplementation @@ -27,6 +28,7 @@ import scala.annotation.strictfp /** * Pravda VM logical pravda.vm.Opcodes implementation. + * * @see pravda.vm.Opcodes * @param memory Access to VM memory * @param wattCounter CPU, memory, storage usage counter @@ -50,9 +52,7 @@ import scala.annotation.strictfp case Int8(data) => Int8((~data).toByte) case Int16(data) => Int16((~data).toShort) case Int32(data) => Int32((~data).toInt) - case Uint8(data) => Uint8((~data) & 0xff) - case Uint16(data) => Uint16((~data) & 0xffff) - case Uint32(data) => Uint32((~data) & 0xffffffff) + case Int64(data) => Int64((~data).toLong) case BigInt(data) => BigInt(~data) case _ => throw ThrowableVmError(WrongType) } @@ -119,83 +119,59 @@ import scala.annotation.strictfp //--------------------------------------------------------------------- private val andImpl: (Data, Data) => Data.Primitive = { (a, b) => a match { + case Int64(lhs) => + b match { + case Int8(rhs) => Int64(lhs & rhs) + case Int16(rhs) => Int64(lhs & rhs) + case Int32(rhs) => Int64(lhs & rhs) + case Int64(rhs) => Int64(lhs & rhs) + case BigInt(rhs) => BigInt(lhs & rhs) + case _ => throw ThrowableVmError(WrongType) + } case Int32(lhs) => b match { case Int8(rhs) => Int32(lhs & rhs) case Int16(rhs) => Int32(lhs & rhs) case Int32(rhs) => Int32(lhs & rhs) - case Uint8(rhs) => Int32(lhs & rhs) - case Uint16(rhs) => Int32(lhs & rhs) - case Uint32(rhs) => Int32((lhs & rhs).toInt) - case BigInt(rhs) => Int32((lhs & rhs).toInt) + case Int64(rhs) => Int64(lhs & rhs) + case BigInt(rhs) => BigInt(lhs & rhs) case _ => throw ThrowableVmError(WrongType) } case Int16(lhs) => b match { case Int8(rhs) => Int16((lhs & rhs).toShort) case Int16(rhs) => Int16((lhs & rhs).toShort) - case Int32(rhs) => Int16((lhs & rhs).toShort) - case Uint8(rhs) => Int16((lhs & rhs).toShort) - case Uint16(rhs) => Int16((lhs & rhs).toShort) - case Uint32(rhs) => Int16((lhs & rhs).toShort) - case BigInt(rhs) => Int16((lhs.toInt & rhs).toShort) + case Int32(rhs) => Int32(lhs & rhs) + case Int64(rhs) => Int64(lhs & rhs) + case BigInt(rhs) => BigInt(lhs.toInt & rhs) case _ => throw ThrowableVmError(WrongType) } case Int8(lhs) => b match { - case Int8(rhs) => Int32(lhs & rhs) - case Int16(rhs) => Int32(lhs & rhs) + case Int8(rhs) => Int8((lhs & rhs).toByte) + case Int16(rhs) => Int16((lhs & rhs).toShort) case Int32(rhs) => Int32(lhs & rhs) - case Uint8(rhs) => Int32(lhs & rhs) - case Uint16(rhs) => Int32(lhs & rhs) - case Uint32(rhs) => BigInt(lhs & rhs) + case Int64(rhs) => Int64(lhs & rhs) case BigInt(rhs) => BigInt(lhs.toInt & rhs) case _ => throw ThrowableVmError(WrongType) } - case Uint8(lhs) => - b match { - case Int8(rhs) => Uint8(lhs & rhs) - case Int16(rhs) => Uint8(lhs & rhs) - case Int32(rhs) => Uint8(lhs & rhs) - case Uint8(rhs) => Uint8(lhs & rhs) - case Uint16(rhs) => Uint8(lhs & rhs) - case Uint32(rhs) => Uint8((lhs & rhs).toInt) - case BigInt(rhs) => Uint8((lhs & rhs).toInt) - case _ => throw ThrowableVmError(WrongType) - } - case Uint16(lhs) => - b match { - case Int8(rhs) => Uint16(lhs & rhs) - case Int16(rhs) => Uint16(lhs & rhs) - case Int32(rhs) => Uint16(lhs & rhs) - case Uint8(rhs) => Uint16(lhs & rhs) - case Uint16(rhs) => Uint16(lhs & rhs) - case Uint32(rhs) => Uint16((lhs & rhs).toInt) - case BigInt(rhs) => Uint16((lhs & rhs).toInt) - case _ => throw ThrowableVmError(WrongType) - } - case Uint32(lhs) => - b match { - case Int8(rhs) => Uint32(lhs & rhs) - case Int16(rhs) => Uint32(lhs & rhs) - case Int32(rhs) => Uint32(lhs & rhs) - case Uint8(rhs) => Uint32(lhs & rhs) - case Uint16(rhs) => Uint32(lhs & rhs) - case Uint32(rhs) => Uint32(lhs & rhs) - case BigInt(rhs) => Uint32((lhs & rhs).toLong) - case _ => throw ThrowableVmError(WrongType) - } case BigInt(lhs) => b match { case Int8(rhs) => BigInt(lhs & scala.BigInt(rhs.toInt)) case Int16(rhs) => BigInt(lhs & scala.BigInt(rhs.toInt)) case Int32(rhs) => BigInt(lhs & rhs) - case Uint8(rhs) => BigInt(lhs & rhs) - case Uint16(rhs) => BigInt(lhs & rhs) - case Uint32(rhs) => BigInt(lhs & rhs) + case Int64(rhs) => BigInt(lhs & rhs) case BigInt(rhs) => BigInt(lhs & rhs) case _ => throw ThrowableVmError(WrongType) } + case Bytes(lhs) => + b match { + case Bytes(rhs) => + Bytes(ByteString.copyFrom(lhs.toByteArray.zipAll(rhs.toByteArray(), 0x00.toByte, 0x00.toByte).map { + case (x: Byte, y: Byte) => (x & y).toByte + })) + case _ => throw ThrowableVmError(WrongType) + } case Bool(lhs) => b match { case Bool(rhs) => Bool(lhs && rhs) @@ -207,83 +183,59 @@ import scala.annotation.strictfp private val orImpl: (Data, Data) => Data.Primitive = { (a, b) => a match { + case Int64(lhs) => + b match { + case Int8(rhs) => Int64(lhs | rhs) + case Int16(rhs) => Int64(lhs | rhs) + case Int32(rhs) => Int64(lhs | rhs) + case Int64(rhs) => Int64(lhs | rhs) + case BigInt(rhs) => BigInt(lhs | rhs) + case _ => throw ThrowableVmError(WrongType) + } case Int32(lhs) => b match { case Int8(rhs) => Int32(lhs | rhs) case Int16(rhs) => Int32(lhs | rhs) case Int32(rhs) => Int32(lhs | rhs) - case Uint8(rhs) => Int32(lhs | rhs) - case Uint16(rhs) => Int32(lhs | rhs) - case Uint32(rhs) => Int32((lhs | rhs).toInt) - case BigInt(rhs) => Int32((lhs | rhs).toInt) + case Int64(rhs) => Int64(lhs | rhs) + case BigInt(rhs) => BigInt(lhs | rhs) case _ => throw ThrowableVmError(WrongType) } case Int16(lhs) => b match { case Int8(rhs) => Int16((lhs | rhs).toShort) case Int16(rhs) => Int16((lhs | rhs).toShort) - case Int32(rhs) => Int16((lhs | rhs).toShort) - case Uint8(rhs) => Int16((lhs | rhs).toShort) - case Uint16(rhs) => Int16((lhs | rhs).toShort) - case Uint32(rhs) => Int16((lhs | rhs).toShort) - case BigInt(rhs) => Int16((lhs.toInt | rhs).toShort) + case Int32(rhs) => Int32(lhs | rhs) + case Int64(rhs) => Int64(lhs | rhs) + case BigInt(rhs) => BigInt(lhs.toInt | rhs) case _ => throw ThrowableVmError(WrongType) } case Int8(lhs) => b match { - case Int8(rhs) => Int32(lhs | rhs) - case Int16(rhs) => Int32(lhs | rhs) + case Int8(rhs) => Int8((lhs | rhs).toByte) + case Int16(rhs) => Int16((lhs | rhs).toShort) case Int32(rhs) => Int32(lhs | rhs) - case Uint8(rhs) => Int32(lhs | rhs) - case Uint16(rhs) => Int32(lhs | rhs) - case Uint32(rhs) => BigInt(lhs | rhs) + case Int64(rhs) => Int64(lhs | rhs) case BigInt(rhs) => BigInt(lhs.toInt | rhs) case _ => throw ThrowableVmError(WrongType) } - case Uint8(lhs) => - b match { - case Int8(rhs) => Uint8(lhs | rhs) - case Int16(rhs) => Uint8(lhs | rhs) - case Int32(rhs) => Uint8(lhs | rhs) - case Uint8(rhs) => Uint8(lhs | rhs) - case Uint16(rhs) => Uint8(lhs | rhs) - case Uint32(rhs) => Uint8((lhs | rhs).toInt) - case BigInt(rhs) => Uint8((lhs | rhs).toInt) - case _ => throw ThrowableVmError(WrongType) - } - case Uint16(lhs) => - b match { - case Int8(rhs) => Uint16(lhs | rhs) - case Int16(rhs) => Uint16(lhs | rhs) - case Int32(rhs) => Uint16(lhs | rhs) - case Uint8(rhs) => Uint16(lhs | rhs) - case Uint16(rhs) => Uint16(lhs | rhs) - case Uint32(rhs) => Uint16((lhs | rhs).toInt) - case BigInt(rhs) => Uint16((lhs | rhs).toInt) - case _ => throw ThrowableVmError(WrongType) - } - case Uint32(lhs) => - b match { - case Int8(rhs) => Uint32(lhs | rhs) - case Int16(rhs) => Uint32(lhs | rhs) - case Int32(rhs) => Uint32(lhs | rhs) - case Uint8(rhs) => Uint32(lhs | rhs) - case Uint16(rhs) => Uint32(lhs | rhs) - case Uint32(rhs) => Uint32(lhs | rhs) - case BigInt(rhs) => Uint32((lhs | rhs).toLong) - case _ => throw ThrowableVmError(WrongType) - } case BigInt(lhs) => b match { case Int8(rhs) => BigInt(lhs | scala.BigInt(rhs.toInt)) case Int16(rhs) => BigInt(lhs | scala.BigInt(rhs.toInt)) case Int32(rhs) => BigInt(lhs | rhs) - case Uint8(rhs) => BigInt(lhs | rhs) - case Uint16(rhs) => BigInt(lhs | rhs) - case Uint32(rhs) => BigInt(lhs | rhs) + case Int64(rhs) => BigInt(lhs | rhs) case BigInt(rhs) => BigInt(lhs | rhs) case _ => throw ThrowableVmError(WrongType) } + case Bytes(lhs) => + b match { + case Bytes(rhs) => + Bytes(ByteString.copyFrom(lhs.toByteArray.zipAll(rhs.toByteArray(), 0x00.toByte, 0x00.toByte).map { + case (x: Byte, y: Byte) => (x | y).toByte + })) + case _ => throw ThrowableVmError(WrongType) + } case Bool(lhs) => b match { case Bool(rhs) => Bool(lhs || rhs) @@ -295,83 +247,59 @@ import scala.annotation.strictfp private val xorImpl: (Data, Data) => Data.Primitive = { (a, b) => a match { + case Int64(lhs) => + b match { + case Int8(rhs) => Int64(lhs ^ rhs) + case Int16(rhs) => Int64(lhs ^ rhs) + case Int32(rhs) => Int64(lhs ^ rhs) + case Int64(rhs) => Int64(lhs ^ rhs) + case BigInt(rhs) => BigInt(lhs ^ rhs) + case _ => throw ThrowableVmError(WrongType) + } case Int32(lhs) => b match { case Int8(rhs) => Int32(lhs ^ rhs) case Int16(rhs) => Int32(lhs ^ rhs) case Int32(rhs) => Int32(lhs ^ rhs) - case Uint8(rhs) => Int32(lhs ^ rhs) - case Uint16(rhs) => Int32(lhs ^ rhs) - case Uint32(rhs) => Int32((lhs ^ rhs).toInt) - case BigInt(rhs) => Int32((lhs ^ rhs).toInt) + case Int64(rhs) => Int64(lhs ^ rhs) + case BigInt(rhs) => BigInt(lhs ^ rhs) case _ => throw ThrowableVmError(WrongType) } case Int16(lhs) => b match { case Int8(rhs) => Int16((lhs ^ rhs).toShort) case Int16(rhs) => Int16((lhs ^ rhs).toShort) - case Int32(rhs) => Int16((lhs ^ rhs).toShort) - case Uint8(rhs) => Int16((lhs ^ rhs).toShort) - case Uint16(rhs) => Int16((lhs ^ rhs).toShort) - case Uint32(rhs) => Int16((lhs ^ rhs).toShort) - case BigInt(rhs) => Int16((lhs.toInt ^ rhs).toShort) + case Int32(rhs) => Int32(lhs ^ rhs) + case Int64(rhs) => Int64(lhs ^ rhs) + case BigInt(rhs) => BigInt(lhs.toInt ^ rhs) case _ => throw ThrowableVmError(WrongType) } case Int8(lhs) => b match { - case Int8(rhs) => Int32(lhs ^ rhs) - case Int16(rhs) => Int32(lhs ^ rhs) + case Int8(rhs) => Int8((lhs ^ rhs).toByte) + case Int16(rhs) => Int16((lhs ^ rhs).toShort) case Int32(rhs) => Int32(lhs ^ rhs) - case Uint8(rhs) => Int32(lhs ^ rhs) - case Uint16(rhs) => Int32(lhs ^ rhs) - case Uint32(rhs) => BigInt(lhs ^ rhs) + case Int64(rhs) => Int64(lhs ^ rhs) case BigInt(rhs) => BigInt(lhs.toInt ^ rhs) case _ => throw ThrowableVmError(WrongType) } - case Uint8(lhs) => - b match { - case Int8(rhs) => Uint8(lhs ^ rhs) - case Int16(rhs) => Uint8(lhs ^ rhs) - case Int32(rhs) => Uint8(lhs ^ rhs) - case Uint8(rhs) => Uint8(lhs ^ rhs) - case Uint16(rhs) => Uint8(lhs ^ rhs) - case Uint32(rhs) => Uint8((lhs ^ rhs).toInt) - case BigInt(rhs) => Uint8((lhs ^ rhs).toInt) - case _ => throw ThrowableVmError(WrongType) - } - case Uint16(lhs) => - b match { - case Int8(rhs) => Uint16(lhs ^ rhs) - case Int16(rhs) => Uint16(lhs ^ rhs) - case Int32(rhs) => Uint16(lhs ^ rhs) - case Uint8(rhs) => Uint16(lhs ^ rhs) - case Uint16(rhs) => Uint16(lhs ^ rhs) - case Uint32(rhs) => Uint16((lhs ^ rhs).toInt) - case BigInt(rhs) => Uint16((lhs ^ rhs).toInt) - case _ => throw ThrowableVmError(WrongType) - } - case Uint32(lhs) => - b match { - case Int8(rhs) => Uint32(lhs ^ rhs) - case Int16(rhs) => Uint32(lhs ^ rhs) - case Int32(rhs) => Uint32(lhs ^ rhs) - case Uint8(rhs) => Uint32(lhs ^ rhs) - case Uint16(rhs) => Uint32(lhs ^ rhs) - case Uint32(rhs) => Uint32(lhs ^ rhs) - case BigInt(rhs) => Uint32((lhs ^ rhs).toLong) - case _ => throw ThrowableVmError(WrongType) - } case BigInt(lhs) => b match { case Int8(rhs) => BigInt(lhs ^ scala.BigInt(rhs.toInt)) case Int16(rhs) => BigInt(lhs ^ scala.BigInt(rhs.toInt)) case Int32(rhs) => BigInt(lhs ^ rhs) - case Uint8(rhs) => BigInt(lhs ^ rhs) - case Uint16(rhs) => BigInt(lhs ^ rhs) - case Uint32(rhs) => BigInt(lhs ^ rhs) + case Int64(rhs) => BigInt(lhs ^ rhs) case BigInt(rhs) => BigInt(lhs ^ rhs) case _ => throw ThrowableVmError(WrongType) } + case Bytes(lhs) => + b match { + case Bytes(rhs) => + Bytes(ByteString.copyFrom(lhs.toByteArray.zipAll(rhs.toByteArray(), 0x00.toByte, 0x00.toByte).map { + case (x: Byte, y: Byte) => (x ^ y).toByte + })) + case _ => throw ThrowableVmError(WrongType) + } case Bool(lhs) => b match { case Bool(rhs) => Bool(lhs ^ rhs) @@ -383,74 +311,42 @@ import scala.annotation.strictfp private val eqImpl: (Data, Data) => Bool = { (a, b) => val result = a match { - case Int32(lhs) => - b match { - case Int8(rhs) => lhs == rhs - case Int16(rhs) => lhs == rhs - case Int32(rhs) => lhs == rhs - case Uint8(rhs) => lhs == rhs - case Uint16(rhs) => lhs == rhs - case Uint32(rhs) => lhs == rhs - case BigInt(rhs) => lhs == rhs - case Number(rhs) => lhs == rhs - case _ => false - } - case Int16(lhs) => - b match { - case Int8(rhs) => lhs == rhs - case Int16(rhs) => lhs == rhs - case Int32(rhs) => lhs == rhs - case Uint8(rhs) => lhs == rhs - case Uint16(rhs) => lhs == rhs - case Uint32(rhs) => lhs == rhs - case BigInt(rhs) => lhs == rhs - case Number(rhs) => lhs == rhs - case _ => false - } - case Int8(lhs) => + case Int64(lhs) => b match { case Int8(rhs) => lhs == rhs case Int16(rhs) => lhs == rhs case Int32(rhs) => lhs == rhs - case Uint8(rhs) => lhs == rhs - case Uint16(rhs) => lhs == rhs - case Uint32(rhs) => lhs == rhs + case Int64(rhs) => lhs == rhs case BigInt(rhs) => lhs == rhs case Number(rhs) => lhs == rhs case _ => false } - case Uint8(lhs) => + case Int32(lhs) => b match { case Int8(rhs) => lhs == rhs case Int16(rhs) => lhs == rhs case Int32(rhs) => lhs == rhs - case Uint8(rhs) => lhs == rhs - case Uint16(rhs) => lhs == rhs - case Uint32(rhs) => lhs == rhs + case Int64(rhs) => lhs == rhs case BigInt(rhs) => lhs == rhs case Number(rhs) => lhs == rhs case _ => false } - case Uint16(lhs) => + case Int16(lhs) => b match { case Int8(rhs) => lhs == rhs case Int16(rhs) => lhs == rhs case Int32(rhs) => lhs == rhs - case Uint8(rhs) => lhs == rhs - case Uint16(rhs) => lhs == rhs - case Uint32(rhs) => lhs == rhs + case Int64(rhs) => lhs == rhs case BigInt(rhs) => lhs == rhs case Number(rhs) => lhs == rhs case _ => false } - case Uint32(lhs) => + case Int8(lhs) => b match { case Int8(rhs) => lhs == rhs case Int16(rhs) => lhs == rhs case Int32(rhs) => lhs == rhs - case Uint8(rhs) => lhs == rhs - case Uint16(rhs) => lhs == rhs - case Uint32(rhs) => lhs == rhs + case Int64(rhs) => lhs == rhs case BigInt(rhs) => lhs == rhs case Number(rhs) => lhs == rhs case _ => false @@ -460,9 +356,7 @@ import scala.annotation.strictfp case Int8(rhs) => lhs == rhs case Int16(rhs) => lhs == rhs case Int32(rhs) => lhs == rhs - case Uint8(rhs) => lhs == rhs - case Uint16(rhs) => lhs == rhs - case Uint32(rhs) => lhs == rhs + case Int64(rhs) => lhs == rhs case BigInt(rhs) => lhs == BigDecimal(rhs) case Number(rhs) => lhs == rhs case _ => false @@ -472,9 +366,7 @@ import scala.annotation.strictfp case Int8(rhs) => lhs == rhs case Int16(rhs) => lhs == rhs case Int32(rhs) => lhs == rhs - case Uint8(rhs) => lhs == rhs - case Uint16(rhs) => lhs == rhs - case Uint32(rhs) => lhs == rhs + case Int64(rhs) => lhs == rhs case BigInt(rhs) => lhs == rhs case Number(rhs) => BigDecimal(lhs) == rhs case _ => false @@ -486,75 +378,43 @@ import scala.annotation.strictfp private val gtImpl: (Data, Data) => Bool = { (a, b) => val result = a match { - case Int32(lhs) => + case Int64(lhs) => b match { case Int8(rhs) => lhs > rhs case Int16(rhs) => lhs > rhs case Int32(rhs) => lhs > rhs - case Uint8(rhs) => lhs > rhs - case Uint16(rhs) => lhs > rhs - case Uint32(rhs) => lhs > rhs - case BigInt(rhs) => lhs > rhs + case Int64(rhs) => lhs > rhs + case BigInt(rhs) => rhs < lhs case Number(rhs) => lhs > rhs case _ => throw ThrowableVmError(WrongType) } - case Int16(lhs) => + case Int32(lhs) => b match { case Int8(rhs) => lhs > rhs case Int16(rhs) => lhs > rhs case Int32(rhs) => lhs > rhs - case Uint8(rhs) => lhs > rhs - case Uint16(rhs) => lhs > rhs - case Uint32(rhs) => lhs > rhs - case BigInt(rhs) => rhs < lhs.toInt + case Int64(rhs) => lhs > rhs + case BigInt(rhs) => rhs < lhs case Number(rhs) => lhs > rhs case _ => throw ThrowableVmError(WrongType) } - case Int8(lhs) => + case Int16(lhs) => b match { case Int8(rhs) => lhs > rhs case Int16(rhs) => lhs > rhs case Int32(rhs) => lhs > rhs - case Uint8(rhs) => lhs > rhs - case Uint16(rhs) => lhs > rhs - case Uint32(rhs) => lhs > rhs + case Int64(rhs) => lhs > rhs case BigInt(rhs) => rhs < lhs.toInt case Number(rhs) => lhs > rhs case _ => throw ThrowableVmError(WrongType) } - case Uint8(lhs) => - b match { - case Int8(rhs) => lhs > rhs - case Int16(rhs) => lhs > rhs - case Int32(rhs) => lhs > rhs - case Uint8(rhs) => lhs > rhs - case Uint16(rhs) => lhs > rhs - case Uint32(rhs) => lhs > rhs - case BigInt(rhs) => lhs > rhs - case Number(rhs) => lhs > rhs - case _ => throw ThrowableVmError(WrongType) - } - case Uint16(lhs) => - b match { - case Int8(rhs) => lhs > rhs - case Int16(rhs) => lhs > rhs - case Int32(rhs) => lhs > rhs - case Uint8(rhs) => lhs > rhs - case Uint16(rhs) => lhs > rhs - case Uint32(rhs) => lhs > rhs - case BigInt(rhs) => lhs > rhs - case Number(rhs) => lhs > rhs - case _ => throw ThrowableVmError(WrongType) - } - case Uint32(lhs) => + case Int8(lhs) => b match { case Int8(rhs) => lhs > rhs case Int16(rhs) => lhs > rhs case Int32(rhs) => lhs > rhs - case Uint8(rhs) => lhs > rhs - case Uint16(rhs) => lhs > rhs - case Uint32(rhs) => lhs > rhs - case BigInt(rhs) => lhs > rhs + case Int64(rhs) => lhs > rhs + case BigInt(rhs) => rhs < lhs.toInt case Number(rhs) => lhs > rhs case _ => throw ThrowableVmError(WrongType) } @@ -563,9 +423,7 @@ import scala.annotation.strictfp case Int8(rhs) => lhs > rhs case Int16(rhs) => lhs > rhs case Int32(rhs) => lhs > rhs - case Uint8(rhs) => lhs > rhs - case Uint16(rhs) => lhs > rhs - case Uint32(rhs) => lhs > rhs + case Int64(rhs) => lhs > rhs case BigInt(rhs) => lhs > BigDecimal(rhs) case Number(rhs) => lhs > rhs case _ => throw ThrowableVmError(WrongType) @@ -575,9 +433,7 @@ import scala.annotation.strictfp case Int8(rhs) => lhs > rhs.toInt case Int16(rhs) => lhs > rhs.toInt case Int32(rhs) => lhs > rhs - case Uint8(rhs) => lhs > rhs - case Uint16(rhs) => lhs > rhs - case Uint32(rhs) => lhs > rhs + case Int64(rhs) => lhs > rhs case BigInt(rhs) => lhs > rhs case Number(rhs) => BigDecimal(lhs) > rhs case _ => throw ThrowableVmError(WrongType) diff --git a/vm/src/main/scala/pravda/vm/operations/StorageOperations.scala b/vm/src/main/scala/pravda/vm/operations/StorageOperations.scala index c90b20ec..eb54209e 100644 --- a/vm/src/main/scala/pravda/vm/operations/StorageOperations.scala +++ b/vm/src/main/scala/pravda/vm/operations/StorageOperations.scala @@ -63,26 +63,59 @@ final class StorageOperations(memory: Memory, maybeStorage: Option[Storage], wat @OpcodeImplementation( opcode = SGET, description = - "Pops first item from stack, interprets it as key, retrieves corresponding record from a storage of the program and pushes the record to the stack. Otherwise throws an exception. " + "Pops first item from stack, interprets it as key, retrieves corresponding" + + " record from a storage of the program and pushes the record to the " + + "stack. Otherwise throws an exception. " ) def get(): Unit = ifStorage { storage => - val data = storage - .get(memory.pop()) - .collect { case p: Data.Primitive => p } - .getOrElse(Data.Primitive.Null) wattCounter.cpuUsage(CpuStorageUse) - wattCounter.memoryUsage(data.volume.toLong) - memory.push(data) + storage.get(memory.pop()) match { + case None => + wattCounter.memoryUsage(1l) + memory.push(Data.Primitive.Null) + case Some(p: Data.Primitive) => + wattCounter.memoryUsage(p.volume.toLong) + memory.push(p) + case Some(s) => + wattCounter.memoryUsage(s.volume.toLong) + memory.push(memory.heapPut(s)) + } } @OpcodeImplementation( opcode = SPUT, description = - "Pops first item from stack, interprets it as key. Pops second item from stack, interprets it as value. Puts (key -> value) record to program's storage. " + "Pops first item from stack, interprets it as key. Pops second item from stack, " + + "interprets it as value. Puts (key -> value) record to program's storage. " + + "If value is a ref, correspondent value will be taken from heap. " + + "Referenced value shouldn't be RefArray and shouldn't be Struct with refs in " + + "field values." ) def put(): Unit = ifStorage { storage => val key = memory.pop() - val value = memory.pop() + val value = memory.pop() match { + case ref: Data.Primitive.Ref => + memory.heapGet(ref) match { + case _: Data.Array.RefArray => + throw ThrowableVmError(Error.WrongType) + case _: Data.Primitive.Ref => + throw ThrowableVmError(Error.WrongType) + case _: Data.Primitive.Offset => + throw ThrowableVmError(Error.WrongType) + case struct: Data.Struct => + // Flat object + val isFlat = struct.data.forall { + case (_, _: Data.Primitive.Ref) => false + case (_, _: Data.Primitive.Offset) => false + case _ => true + } + if (isFlat) struct + else throw ThrowableVmError(Error.WrongType) + case x => x + } + case x: Data.Primitive => x + case _ => throw ThrowableVmError(Error.WrongType) + } val bytesTotal = value.volume + key.volume val maybePrevious = storage.put(key, value) diff --git a/vm/src/main/scala/pravda/vm/operations/SystemOperations.scala b/vm/src/main/scala/pravda/vm/operations/SystemOperations.scala index ac8e5865..6867bc67 100644 --- a/vm/src/main/scala/pravda/vm/operations/SystemOperations.scala +++ b/vm/src/main/scala/pravda/vm/operations/SystemOperations.scala @@ -230,7 +230,7 @@ final class SystemOperations(program: ByteBuffer, "create new event with name as given string and with given data.") def event(): Unit = { - def marshalData(data: Data) = { + def marshalData(data: Data): (Data, Map[Data.Primitive.Ref, Data]) = { // (Original -> (UpdatedData, AssignedRef)) val pHeap = mutable.Map.empty[Data.Primitive.Ref, (Data.Primitive.Ref, Data)] def extract(ref: Data.Primitive.Ref) = { @@ -277,6 +277,40 @@ final class SystemOperations(program: ByteBuffer, case None => throw ThrowableVmError(OperationDenied) } } + + @OpcodeImplementation( + opcode = CALLERS, + description = "Gets caller's 'call stack' (see CALL opcode) " + + "and pushes it to the stack" + ) + def callers(): Unit = { + val cs = Data.Array.BytesArray( + memory.callStack.flatMap(_._1).toBuffer + ) + wattCounter.memoryUsage(cs.volume.toLong) + wattCounter.cpuUsage(CpuStorageUse) + memory.push(memory.heapPut(cs)) + } + + @OpcodeImplementation( + opcode = HEIGHT, + description = "Gets current height of the blockchain and pushes it to the stack." + ) + def chainHeight(): Unit = { + val data = env.chainHeight + wattCounter.cpuUsage(CpuStorageUse) + memory.push(Data.Primitive.Int64(data)) + } + + @OpcodeImplementation( + opcode = HASH, + description = "Gets hash of the last block and pushes it to the stack." + ) + def lastBlockHash(): Unit = { + val data = env.lastBlockHash + wattCounter.cpuUsage(CpuStorageUse) + memory.push(Data.Primitive.Bytes(data)) + } } object SystemOperations { diff --git a/vm/src/main/scala/pravda/vm/operations/package.scala b/vm/src/main/scala/pravda/vm/operations/package.scala index e86593ec..fc18c7a3 100644 --- a/vm/src/main/scala/pravda/vm/operations/package.scala +++ b/vm/src/main/scala/pravda/vm/operations/package.scala @@ -20,13 +20,14 @@ package pravda.vm import com.google.protobuf.ByteString import pravda.common.domain.{Address, NativeCoin} import pravda.vm.Data.Primitive._ -import pravda.vm.Error.{InvalidAddress, InvalidCoinAmount, WrongType} +import pravda.vm.Error.{InvalidAddress, WrongType} package object operations { /** * Applies `f` to two top items from stack. * Pushes application result to stack. + * * @param f binary operation */ def binaryOperation(memory: Memory, wattCounter: WattCounter)(f: (Data, Data) => Data.Primitive): Unit = { @@ -51,9 +52,7 @@ package object operations { case Int8(x) => x.toLong case Int16(x) => x.toLong case Int32(x) => x.toLong - case Uint8(x) => x.toLong - case Uint16(x) => x.toLong - case Uint32(x) => x.toLong + case Int64(x) => x case BigInt(x) => x.toLong case _ => throw ThrowableVmError(WrongType) } @@ -81,13 +80,11 @@ package object operations { def bytes(a: ByteString): Bytes = Bytes(a) def coins(a: Data): NativeCoin = a match { - case BigInt(data) if data < Long.MinValue || data > Long.MaxValue => throw ThrowableVmError(InvalidCoinAmount) - case BigInt(data) => NativeCoin @@ data.toLong - case _ => throw ThrowableVmError(WrongType) + case Int64(data) => NativeCoin @@ data + case _ => throw ThrowableVmError(WrongType) } - def coins(a: NativeCoin): Data.Primitive = - BigInt(scala.BigInt(a)) + def coins(a: NativeCoin): Data.Primitive = Int64(a) def address(a: Data): Address = { val bytes = a match { diff --git a/vm/src/main/scala/pravda/vm/standard/BytesToHex.scala b/vm/src/main/scala/pravda/vm/standard/BytesToHex.scala new file mode 100644 index 00000000..d58f4b29 --- /dev/null +++ b/vm/src/main/scala/pravda/vm/standard/BytesToHex.scala @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2018 Expload.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package pravda.vm.standard + +import pravda.vm.Data.Type +import pravda.vm.WattCounter.CpuArithmetic +import pravda.vm._ +import pravda.vm.operations._ + +object BytesToHex extends FunctionDefinition { + + val id = 0x05L + + val description = + "Takes bytes from stack, pushes hex string to the stack" + + val args: Seq[(String, Seq[Type])] = Seq( + "bytes" -> Seq(Data.Type.Bytes), + ) + + val returns = Seq(Data.Type.Utf8) + + def apply(memory: Memory, wattCounter: WattCounter): Unit = { + val b = bytes(memory.pop()) + wattCounter.cpuUsage(CpuArithmetic * b.size()) + wattCounter.memoryUsage(b.size().toLong * 2) + + val result = try { + pravda.common.bytes.bytes2hex(b.toByteArray) + } catch { + case _: Throwable => + throw ThrowableVmError(Error.InvalidArgument) + } + + memory.push(Data.Primitive.Utf8(result)) + } +} diff --git a/vm/src/main/scala/pravda/vm/standard/ExpandBytesEvm.scala b/vm/src/main/scala/pravda/vm/standard/ExpandBytesEvm.scala new file mode 100644 index 00000000..68acce1f --- /dev/null +++ b/vm/src/main/scala/pravda/vm/standard/ExpandBytesEvm.scala @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2018 Expload.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package pravda.vm.standard + +import com.google.protobuf.ByteString +import pravda.vm.Data.Type +import pravda.vm.Error.InvalidArgument +import pravda.vm.WattCounter.CpuArithmetic +import pravda.vm._ +import pravda.vm.operations._ + +object ExpandBytesEvm extends FunctionDefinition { + + val id = 0x08L + + val description = + "Takes bytes from stack. Return expanded to 32 length bytes. " + + val args: Seq[(String, Seq[Type])] = Seq( + "bytes" -> Seq(Data.Type.Bytes) + ) + + val returns = Seq(Data.Type.Bytes) + + def apply(memory: Memory, wattCounter: WattCounter): Unit = { + val bs = bytes(memory.pop()) + val res = if (bs.size() > 32) { + throw ThrowableVmError(InvalidArgument) + } else { + Data.Primitive.Bytes(bs.concat(ByteString.copyFrom(Array.fill[Byte](32 - bs.size())(0)))) + } + + wattCounter.cpuUsage(CpuArithmetic) + wattCounter.memoryUsage(32L) + + memory.push(res) + } +} diff --git a/vm/src/main/scala/pravda/vm/standard/ExponentialFunction.scala b/vm/src/main/scala/pravda/vm/standard/ExponentialFunction.scala index 120f7006..235c926a 100644 --- a/vm/src/main/scala/pravda/vm/standard/ExponentialFunction.scala +++ b/vm/src/main/scala/pravda/vm/standard/ExponentialFunction.scala @@ -30,37 +30,41 @@ object ExponentialFunction extends FunctionDefinition { "Takes two items from the stack, raises the second number to a power of first number and pushes the result to the stack." val args: Seq[(String, Seq[Type])] = Seq( - "x" -> Seq(Data.Type.Int32), - "y" -> Seq(Data.Type.Int32) + "x" -> Seq(Data.Type.Int8, Data.Type.Int16, Data.Type.Int32, Data.Type.Int64, Data.Type.Number, Data.Type.BigInt), + "y" -> Seq(Data.Type.Int8, Data.Type.Int16, Data.Type.Int32, Data.Type.Number, Data.Type.BigInt) ) - val returns = Seq(Data.Type.Number) + val returns = + Seq(Data.Type.Int8, Data.Type.Int16, Data.Type.Int32, Data.Type.Int64, Data.Type.Number, Data.Type.BigInt) - private def pow(x: Double, y: Double): Double = math.pow(x, y) - private def pow(x: Double, y: Byte): Double = math.pow(x, y.toDouble) - private def pow(x: Double, y: Int): Double = math.pow(x, y.toDouble) - private def pow(x: Double, y: Long): Double = math.pow(x, y.toDouble) - private def pow(x: Double, y: Short): Double = math.pow(x, y.toDouble) - private def pow(x: Long, y: Double): Long = math.pow(x.toDouble, y).toLong - private def pow(x: Long, y: Byte): Long = math.pow(x.toDouble, y.toDouble).toLong - private def pow(x: Long, y: Int): Long = math.pow(x.toDouble, y.toDouble).toLong - private def pow(x: Long, y: Long): Long = math.pow(x.toDouble, y.toDouble).toLong - private def pow(x: Long, y: Short): Long = math.pow(x.toDouble, y.toDouble).toLong - private def pow(x: Int, y: Double): Int = math.pow(x.toDouble, y).toInt - private def pow(x: Int, y: Byte): Int = math.pow(x.toDouble, y.toDouble).toInt - private def pow(x: Int, y: Int): Int = math.pow(x.toDouble, y.toDouble).toInt - private def pow(x: Int, y: Long): Int = math.pow(x.toDouble, y.toDouble).toInt - private def pow(x: Int, y: Short): Int = math.pow(x.toDouble, y.toDouble).toInt - private def pow(x: Short, y: Double): Short = math.pow(x.toDouble, y).toShort - private def pow(x: Short, y: Byte): Short = math.pow(x.toDouble, y.toDouble).toShort - private def pow(x: Short, y: Int): Short = math.pow(x.toDouble, y.toDouble).toShort - private def pow(x: Short, y: Long): Short = math.pow(x.toDouble, y.toDouble).toShort - private def pow(x: Short, y: Short): Short = math.pow(x.toDouble, y.toDouble).toShort - private def pow(x: Byte, y: Double): Int = math.pow(x.toDouble, y).toInt - private def pow(x: Byte, y: Byte): Int = math.pow(x.toDouble, y.toDouble).toInt - private def pow(x: Byte, y: Int): Int = math.pow(x.toDouble, y.toDouble).toInt - private def pow(x: Byte, y: Long): Int = math.pow(x.toDouble, y.toDouble).toInt - private def pow(x: Byte, y: Short): Int = math.pow(x.toDouble, y.toDouble).toInt + private def pow(x: Long, y: Int): Long = y match { + case p if p <= 0 => 1L + case p if p % 2 == 1 => x * pow(x, y - 1) + case p if p % 2 == 0 => + val r = pow(x, p / 2) + r * r + } + private def pow(x: Int, y: Int): Int = y match { + case p if p <= 0 => 1 + case p if p % 2 == 1 => x * pow(x, y - 1) + case p if p % 2 == 0 => + val r = pow(x, p / 2) + r * r + } + private def pow(x: Short, y: Int): Short = y match { + case p if p <= 0 => 1 + case p if p % 2 == 1 => (x * pow(x, y - 1)).toShort + case p if p % 2 == 0 => + val r = pow(x, p / 2) + (r * r).toShort + } + private def pow(x: Byte, y: Int): Byte = y match { + case p if p <= 0 => 1 + case p if p % 2 == 1 => (x * pow(x, y - 1)).toByte + case p if p % 2 == 0 => + val r = pow(x, p / 2) + (r * r).toByte + } def apply(memory: Memory, wattCounter: WattCounter): Unit = { @@ -69,191 +73,91 @@ object ExponentialFunction extends FunctionDefinition { primitive } - def calculateWattsForBigInt(x: scala.BigInt, y: Int): Long = { - (scala.BigInt(x.bitLength) * y.abs / 8 + 1).toLong //FIXME overflow - } + def wattsForBigInt(x: scala.BigInt, y: Int): Long = + x.bitLength * y.abs.toLong / 8 + 1 + wattCounter.cpuUsage(CpuArithmetic) val a = memory.pop() val b = memory.pop() val res = a match { - case Int32(lhs) => + case Int64(lhs) => b match { case Int8(rhs) => - calculateWatts(Int32(pow(lhs, rhs))) + calculateWatts(Int64(pow(lhs, rhs.toInt))) case Int16(rhs) => - calculateWatts(Int32(pow(lhs, rhs))) + calculateWatts(Int64(pow(lhs, rhs.toInt))) case Int32(rhs) => - calculateWatts(Int32(pow(lhs, rhs))) - case Uint8(rhs) => - calculateWatts(Int32(pow(lhs, rhs))) - case Uint16(rhs) => - calculateWatts(Int32(pow(lhs, rhs))) - case Uint32(rhs) => - calculateWatts(Int32(pow(lhs, rhs))) - case BigInt(rhs) => - calculateWatts(Int32(pow(lhs, rhs.intValue()))) + calculateWatts(Int64(pow(lhs, rhs))) case Number(rhs) => - calculateWatts(Int32(pow(lhs, rhs))) + calculateWatts(Number(math.pow(lhs.toDouble, rhs))) case _ => throw ThrowableVmError(WrongType) } - case Int16(lhs) => - b match { - case Int8(rhs) => - calculateWatts(Int16(pow(lhs, rhs))) - case Int16(rhs) => - calculateWatts(Int16(pow(lhs, rhs))) - case Int32(rhs) => - calculateWatts(Int16(pow(lhs, rhs))) - case Uint8(rhs) => - calculateWatts(Int16(pow(lhs, rhs))) - case Uint16(rhs) => - calculateWatts(Int16(pow(lhs, rhs))) - case Uint32(rhs) => - calculateWatts(Int16(pow(lhs, rhs))) - case BigInt(rhs) => - calculateWatts(Int16(pow(lhs, rhs.intValue()))) - case Number(rhs) => - calculateWatts(Int16(pow(lhs, rhs))) - case _ => throw ThrowableVmError(WrongType) - - } - case Int8(lhs) => + case Int32(lhs) => b match { case Int8(rhs) => - calculateWatts(Int32(pow(lhs, rhs))) + calculateWatts(Int32(pow(lhs, rhs.toInt))) case Int16(rhs) => - calculateWatts(Int32(pow(lhs, rhs))) + calculateWatts(Int32(pow(lhs, rhs.toInt))) case Int32(rhs) => calculateWatts(Int32(pow(lhs, rhs))) - case Uint8(rhs) => - calculateWatts(Int32(pow(lhs, rhs))) - case Uint16(rhs) => - calculateWatts(Int32(pow(lhs, rhs))) - case Uint32(rhs) => - calculateWatts(Int32(pow(lhs, rhs))) - case BigInt(rhs) => - calculateWatts(Int32(pow(lhs, rhs.intValue()))) - case Number(rhs) => - calculateWatts(Number(pow(lhs, rhs).toDouble)) - case _ => throw ThrowableVmError(WrongType) - - } - case Uint8(lhs) => - b match { - case Int8(rhs) => - calculateWatts(Uint8(pow(lhs, rhs))) - case Int16(rhs) => - calculateWatts(Uint8(pow(lhs, rhs))) - case Int32(rhs) => - calculateWatts(Uint8(pow(lhs, rhs))) - case Uint8(rhs) => - calculateWatts(Uint8(pow(lhs, rhs))) - case Uint16(rhs) => - calculateWatts(Uint8(pow(lhs, rhs))) - case Uint32(rhs) => - calculateWatts(Uint8(pow(lhs, rhs))) - case BigInt(rhs) => - calculateWatts(Uint8(pow(lhs, rhs.byteValue()))) case Number(rhs) => - calculateWatts(Uint8(pow(lhs, rhs))) + calculateWatts(Number(math.pow(lhs.toDouble, rhs))) case _ => throw ThrowableVmError(WrongType) - } - case Uint16(lhs) => + case Int16(lhs) => b match { case Int8(rhs) => - calculateWatts(Uint16(pow(lhs, rhs))) + calculateWatts(Int16(pow(lhs, rhs.toInt))) case Int16(rhs) => - calculateWatts(Uint16(pow(lhs, rhs))) + calculateWatts(Int16(pow(lhs, rhs.toInt))) case Int32(rhs) => - calculateWatts(Uint16(pow(lhs, rhs))) - case Uint8(rhs) => - calculateWatts(Uint16(pow(lhs, rhs))) - case Uint16(rhs) => - calculateWatts(Uint16(pow(lhs, rhs))) - case Uint32(rhs) => - calculateWatts(Uint16(pow(lhs, rhs))) - case BigInt(rhs) => - calculateWatts(Uint16(pow(lhs, rhs.intValue()))) + calculateWatts(Int16(pow(lhs, rhs))) case Number(rhs) => - calculateWatts(Uint16(pow(lhs, rhs))) + calculateWatts(Number(math.pow(lhs.toDouble, rhs))) case _ => throw ThrowableVmError(WrongType) - } - case Uint32(lhs) => + case Int8(lhs) => b match { case Int8(rhs) => - calculateWatts(Uint32(pow(lhs, rhs))) + calculateWatts(Int8(pow(lhs, rhs.toInt))) case Int16(rhs) => - calculateWatts(Uint32(pow(lhs, rhs))) + calculateWatts(Int8(pow(lhs, rhs.toInt))) case Int32(rhs) => - calculateWatts(Uint32(pow(lhs, rhs))) - case Uint8(rhs) => - calculateWatts(Uint32(pow(lhs, rhs))) - case Uint16(rhs) => - calculateWatts(Uint32(pow(lhs, rhs))) - case Uint32(rhs) => - calculateWatts(Uint32(pow(lhs, rhs))) - case BigInt(rhs) => - calculateWatts(Uint32(pow(lhs, rhs.intValue()))) + calculateWatts(Int8(pow(lhs, rhs))) case Number(rhs) => - calculateWatts(Uint32(pow(lhs, rhs))) + calculateWatts(Number(math.pow(lhs.toDouble, rhs))) case _ => throw ThrowableVmError(WrongType) - } case Number(lhs) => b match { case Int8(rhs) => - calculateWatts(Number(pow(lhs, rhs))) + calculateWatts(Number(math.pow(lhs, rhs.toDouble))) case Int16(rhs) => - calculateWatts(Number(pow(lhs, rhs))) + calculateWatts(Number(math.pow(lhs, rhs.toDouble))) case Int32(rhs) => - calculateWatts(Number(pow(lhs, rhs))) - case Uint8(rhs) => - calculateWatts(Number(pow(lhs, rhs))) - case Uint16(rhs) => - calculateWatts(Number(pow(lhs, rhs))) - case Uint32(rhs) => - calculateWatts(Number(pow(lhs, rhs))) - case BigInt(rhs) => - calculateWatts(Number(pow(lhs, rhs.longValue()))) + calculateWatts(Number(math.pow(lhs, rhs.toDouble))) case Number(rhs) => - calculateWatts(Number(pow(lhs, rhs))) + calculateWatts(Number(math.pow(lhs, rhs))) case _ => throw ThrowableVmError(WrongType) - } case BigInt(lhs) => b match { case Int8(rhs) => - wattCounter.memoryUsage(calculateWattsForBigInt(lhs, rhs.intValue())) + wattCounter.memoryUsage(wattsForBigInt(lhs, rhs.toInt)) BigInt(lhs.pow(rhs.toInt)) case Int16(rhs) => - wattCounter.memoryUsage(calculateWattsForBigInt(lhs, rhs.intValue)) + wattCounter.memoryUsage(wattsForBigInt(lhs, rhs.toInt)) BigInt(lhs.pow(rhs.toInt)) case Int32(rhs) => - wattCounter.memoryUsage(calculateWattsForBigInt(lhs, rhs.intValue)) - BigInt(lhs.pow(rhs)) - case Uint8(rhs) => - wattCounter.memoryUsage(calculateWattsForBigInt(lhs, rhs.intValue)) - BigInt(lhs.pow(rhs)) - case Uint16(rhs) => - wattCounter.memoryUsage(calculateWattsForBigInt(lhs, rhs.intValue)) + wattCounter.memoryUsage(wattsForBigInt(lhs, rhs)) BigInt(lhs.pow(rhs)) - case Uint32(rhs) => - wattCounter.memoryUsage(calculateWattsForBigInt(lhs, rhs.intValue)) - BigInt(lhs.pow(rhs.toInt)) case BigInt(rhs) => - wattCounter.memoryUsage(calculateWattsForBigInt(lhs, rhs.intValue())) - BigInt(lhs.pow(rhs.intValue())) - case Number(rhs) => - wattCounter.memoryUsage(calculateWattsForBigInt(lhs, rhs.intValue)) + wattCounter.memoryUsage(wattsForBigInt(lhs, rhs.toInt)) BigInt(lhs.pow(rhs.toInt)) case _ => throw ThrowableVmError(WrongType) - } case _ => throw ThrowableVmError(WrongType) - } memory.push(res) diff --git a/vm/src/main/scala/pravda/vm/standard/Sha3.scala b/vm/src/main/scala/pravda/vm/standard/Sha3.scala new file mode 100644 index 00000000..aa8a6f3f --- /dev/null +++ b/vm/src/main/scala/pravda/vm/standard/Sha3.scala @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2018 Expload.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package pravda.vm.standard + +import java.nio.charset.StandardCharsets + +import com.google.protobuf.ByteString +import org.bouncycastle.jcajce.provider.digest._ +import pravda.vm._ + +object Sha3 extends FunctionDefinition { + + val id: Long = 0x09L + + val description: String = + "Calculate Keccak-256 hash for message." + + val returns = Seq(Data.Type.Bytes) + + val args = Seq( + "message" -> Seq(Data.Type.Bytes, Data.Type.Utf8) + ) + + def apply(memory: Memory, wattCounter: WattCounter): Unit = { + val message = memory.pop() match { + case Data.Primitive.Bytes(data) => data.toByteArray + case Data.Primitive.Utf8(data) => data.getBytes(StandardCharsets.UTF_8) + case _ => throw ThrowableVmError(Error.WrongType) + } + val digest = new Keccak.Digest256 + val result = digest.digest(message) + wattCounter.cpuUsage(message.length * WattCounter.CpuArithmetic) + memory.push(Data.Primitive.Bytes(ByteString.copyFrom(result))) + } +} diff --git a/vm/src/main/scala/pravda/vm/standard/SliceByteArray.scala b/vm/src/main/scala/pravda/vm/standard/SliceByteArray.scala new file mode 100644 index 00000000..1434cf91 --- /dev/null +++ b/vm/src/main/scala/pravda/vm/standard/SliceByteArray.scala @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2018 Expload.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package pravda.vm.standard + +import com.google.protobuf.ByteString +import pravda.vm.Data.Array.Int8Array +import pravda.vm.Data.Type +import pravda.vm.Error.{InvalidArgument, WrongType} +import pravda.vm.WattCounter.CpuArithmetic +import pravda.vm._ +import pravda.vm.operations._ + +object SliceByteArray extends FunctionDefinition { + + val id = 0x06L + + val description = + "Takes byte array, index and size from stack. Returns size bytes from given index in the given array." + + val args: Seq[(String, Seq[Type])] = Seq( + "size" -> Seq(Data.Type.BigInt), + "index" -> Seq(Data.Type.BigInt), + "array" -> Seq(Data.Type.Array) + ) + + val returns = Seq(Data.Type.Bytes) + + def apply(memory: Memory, wattCounter: WattCounter): Unit = { + val res = memory.heapGet(ref(memory.pop())) match { + case Int8Array(data) => + val ind = memory.pop() match { + case Data.Primitive.BigInt(b) => + if (b < 0) throw ThrowableVmError(InvalidArgument) + else if (b > Int.MaxValue) Int.MaxValue + else b.toInt + case _ => throw ThrowableVmError(WrongType) + } + + val size = memory.pop() match { + case Data.Primitive.BigInt(b) => + if (b < 0) throw ThrowableVmError(InvalidArgument) + else if (b > Int.MaxValue) Int.MaxValue + else b.toInt + case _ => throw ThrowableVmError(WrongType) + } + Data.Primitive.Bytes(ByteString.copyFrom(data.slice(ind, ind + size).toArray)) + case _ => throw ThrowableVmError(WrongType) + } + + wattCounter.cpuUsage(CpuArithmetic) + wattCounter.memoryUsage(res.data.size().toLong * 2) + + memory.push(res) + } +} diff --git a/vm/src/main/scala/pravda/vm/standard/WriteSliceByteArray.scala b/vm/src/main/scala/pravda/vm/standard/WriteSliceByteArray.scala new file mode 100644 index 00000000..529ede81 --- /dev/null +++ b/vm/src/main/scala/pravda/vm/standard/WriteSliceByteArray.scala @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2018 Expload.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package pravda.vm.standard + +import pravda.vm.Data.Array.Int8Array +import pravda.vm.Data.Type +import pravda.vm.Error.{InvalidArgument, WrongType} +import pravda.vm.WattCounter.CpuArithmetic +import pravda.vm._ +import pravda.vm.operations._ + +object WriteSliceByteArray extends FunctionDefinition { + + val id = 0x07L + + val description = + "Takes byte array, index from stack, bytes to write. " + + "Writes given bytes from given index in the given array. " + + "Returns reference to array" + + val args: Seq[(String, Seq[Type])] = Seq( + "bytes" -> Seq(Data.Type.Bytes), + "index" -> Seq(Data.Type.BigInt), + "array" -> Seq(Data.Type.Array) + ) + + val returns = Seq(Data.Type.Array) + + def apply(memory: Memory, wattCounter: WattCounter): Unit = { + val r = ref(memory.pop()) + val res = memory.heapGet(r) match { + case Int8Array(data) => + val ind = memory.pop() match { + case Data.Primitive.BigInt(b) => + if (b < 0) throw ThrowableVmError(InvalidArgument) + else if (b > data.length) throw ThrowableVmError(InvalidArgument) + else b.toInt + case _ => throw ThrowableVmError(WrongType) + } + + val bytes = memory.pop() match { + case Data.Primitive.Bytes(b) => + if (ind + b.size() > data.length) throw ThrowableVmError(InvalidArgument) + else b + case _ => throw ThrowableVmError(WrongType) + } + + bytes.toByteArray.zipWithIndex.foreach { + case (b, i) => data(ind + i) = b + } + + wattCounter.cpuUsage(bytes.size() * CpuArithmetic) + + r + + case _ => throw ThrowableVmError(WrongType) + } + + memory.push(res) + } +} diff --git a/vm/src/test/resources/callers.asm b/vm/src/test/resources/callers.asm new file mode 100644 index 00000000..d3cfdf20 --- /dev/null +++ b/vm/src/test/resources/callers.asm @@ -0,0 +1,3 @@ +push x1eaed20b7ce2b336043e4b340b031f95bb1ce6d935ef733ae4df1b66e1e3d91f +push 0 +pcall \ No newline at end of file diff --git a/vm/src/test/resources/opcodes/add.sbox b/vm/src/test/resources/opcodes/add.sbox index bcb4eedf..6dbc569c 100644 --- a/vm/src/test/resources/opcodes/add.sbox +++ b/vm/src/test/resources/opcodes/add.sbox @@ -1,7 +1,8 @@ -watts-limit: 1000 -stack: - - int32.2 - - int32.2 +vm: + watts-limit: 1000 + stack: + - int32.2 + - int32.2 code: |- add --- diff --git a/vm/src/test/resources/opcodes/addWithError.sbox b/vm/src/test/resources/opcodes/addWithError.sbox index 62f1392d..6cb6964b 100644 --- a/vm/src/test/resources/opcodes/addWithError.sbox +++ b/vm/src/test/resources/opcodes/addWithError.sbox @@ -1,7 +1,8 @@ -watts-limit: 1000 -stack: - - int32.2 - - "null" +vm: + watts-limit: 1000 + stack: + - int32.2 + - "null" code: |- add --- diff --git a/vm/src/test/resources/opcodes/app-state-info.sbox b/vm/src/test/resources/opcodes/app-state-info.sbox new file mode 100644 index 00000000..41d04a8d --- /dev/null +++ b/vm/src/test/resources/opcodes/app-state-info.sbox @@ -0,0 +1,13 @@ +vm: + watts-limit: 1000 + app-state-info: + app-hash: 62099c6a16853f70fcf2e5a24da6e46faaf0b2541658bec668527b0436d32ece + height: 4 +code: |- + height + hash +--- +watts-spent: 42 +stack: + - int64.4 + - bytes.62099c6a16853f70fcf2e5a24da6e46faaf0b2541658bec668527b0436d32ece \ No newline at end of file diff --git a/vm/src/test/resources/opcodes/array-out-of-bounds-1.sbox b/vm/src/test/resources/opcodes/array-out-of-bounds-1.sbox new file mode 100644 index 00000000..1e73cf31 --- /dev/null +++ b/vm/src/test/resources/opcodes/array-out-of-bounds-1.sbox @@ -0,0 +1,19 @@ +vm: + watts-limit: 1000 +code: |- + new int8[1,2,3] + push int8(10) + push 3 + array_mut +--- +watts-spent: 104 +heap: + ref.0: + - int8 + - '1' + - '2' + - '3' +error: + code: 107 + message: InvalidArgument + diff --git a/vm/src/test/resources/opcodes/array-out-of-bounds-2.sbox b/vm/src/test/resources/opcodes/array-out-of-bounds-2.sbox new file mode 100644 index 00000000..b598b119 --- /dev/null +++ b/vm/src/test/resources/opcodes/array-out-of-bounds-2.sbox @@ -0,0 +1,18 @@ +vm: + watts-limit: 1000 +code: |- + new int8[1,2,3] + push -1 + array_get +--- +watts-spent: 103 +heap: + ref.0: + - int8 + - '1' + - '2' + - '3' +error: + code: 107 + message: InvalidArgument + diff --git a/vm/src/test/resources/opcodes/array-out-of-bounds-3.sbox b/vm/src/test/resources/opcodes/array-out-of-bounds-3.sbox new file mode 100644 index 00000000..f0d404a6 --- /dev/null +++ b/vm/src/test/resources/opcodes/array-out-of-bounds-3.sbox @@ -0,0 +1,18 @@ +vm: + watts-limit: 1000 +code: |- + new int8[1,2,3] + push 3 + array_get +--- +watts-spent: 103 +heap: + ref.0: + - int8 + - '1' + - '2' + - '3' +error: + code: 107 + message: InvalidArgument + diff --git a/vm/src/test/resources/opcodes/balance.sbox b/vm/src/test/resources/opcodes/balance.sbox index 388d3084..332c33fc 100644 --- a/vm/src/test/resources/opcodes/balance.sbox +++ b/vm/src/test/resources/opcodes/balance.sbox @@ -1,13 +1,14 @@ -watts-limit: 1000 -balances: - 25a0e8fe9e30d91c4f5a5ef3dbb405ac65176901ae453771b27c3c83c714587d: bigint.42 +vm: + watts-limit: 1000 + balances: + 25a0e8fe9e30d91c4f5a5ef3dbb405ac65176901ae453771b27c3c83c714587d: int64.42 code: |- push x25a0e8fe9e30d91c4f5a5ef3dbb405ac65176901ae453771b27c3c83c714587d balance --- watts-spent: 122 stack: - - bigint.42 + - int64.42 effects: - eventType: ShowBalance address: 25a0e8fe9e30d91c4f5a5ef3dbb405ac65176901ae453771b27c3c83c714587d diff --git a/vm/src/test/resources/opcodes/call-ret.sbox b/vm/src/test/resources/opcodes/call-ret.sbox index 50a4834e..353ec801 100644 --- a/vm/src/test/resources/opcodes/call-ret.sbox +++ b/vm/src/test/resources/opcodes/call-ret.sbox @@ -1,4 +1,5 @@ -watts-limit: 1000 +vm: + watts-limit: 1000 code: |- jump @main @proc: @@ -11,4 +12,4 @@ code: |- --- watts-spent: 130 stack: - - int32.5 + - int8.5 diff --git a/vm/src/test/resources/opcodes/callers.sbox b/vm/src/test/resources/opcodes/callers.sbox new file mode 100644 index 00000000..234cefa3 --- /dev/null +++ b/vm/src/test/resources/opcodes/callers.sbox @@ -0,0 +1,26 @@ +vm: + watts-limit: 300 + storage: + utf8.init: "null" + program-storage: + "1eaed20b7ce2b336043e4b340b031f95bb1ce6d935ef733ae4df1b66e1e3d91f": + utf8.init: "null" + "eeeae019052822f37d6ecac34ff7f2c6b98d500a14da1ff968366aa552d2ef4c": + utf8.init: "null" + programs: + "1eaed20b7ce2b336043e4b340b031f95bb1ce6d935ef733ae4df1b66e1e3d91f": bytes.ab + "eeeae019052822f37d6ecac34ff7f2c6b98d500a14da1ff968366aa552d2ef4c": bytes.110e601eaed20b7ce2b336043e4b340b031f95bb1ce6d935ef733ae4df1b66e1e3d91f11050006 +code: |- + push xeeeae019052822f37d6ecac34ff7f2c6b98d500a14da1ff968366aa552d2ef4c + push 0 + pcall +--- +watts-spent: 227 +stack: + - ref.0 +heap: + ref.0: + - bytes + - '0000000000000000000000000000000000000000000000000000000000000000' + - eeeae019052822f37d6ecac34ff7f2c6b98d500a14da1ff968366aa552d2ef4c + - 1eaed20b7ce2b336043e4b340b031f95bb1ce6d935ef733ae4df1b66e1e3d91f \ No newline at end of file diff --git a/vm/src/test/resources/opcodes/code.sbox b/vm/src/test/resources/opcodes/code.sbox index 184da0ac..a36e628d 100644 --- a/vm/src/test/resources/opcodes/code.sbox +++ b/vm/src/test/resources/opcodes/code.sbox @@ -1,6 +1,7 @@ -watts-limit: 1000 -programs: - 2AC0014F233C9CA1B2E13EC18D8626C6DFC301B37346053E809EE255EE2C680D: bytes.FFFF +vm: + watts-limit: 1000 + programs: + 2AC0014F233C9CA1B2E13EC18D8626C6DFC301B37346053E809EE255EE2C680D: bytes.FFFF code: |- push x2AC0014F233C9CA1B2E13EC18D8626C6DFC301B37346053E809EE255EE2C680D code diff --git a/vm/src/test/resources/opcodes/complex-event.sbox b/vm/src/test/resources/opcodes/complex-event.sbox new file mode 100644 index 00000000..13177cea --- /dev/null +++ b/vm/src/test/resources/opcodes/complex-event.sbox @@ -0,0 +1,21 @@ +vm: + watts-limit: 1000 +code: |- + new { "year": 2018, "name": "Expload foundation" } + push "world_event" + event +--- +watts-spent: 103 +heap: + ref.0: + utf8.name: utf8.Expload foundation + utf8.year: int16.2018 +effects: + - eventType: Event + program: '0000000000000000000000000000000000000000000000000000000000000000' + name: world_event + data: + simple: + data: + utf8.name: utf8.Expload foundation + utf8.year: int16.2018 \ No newline at end of file diff --git a/vm/src/test/resources/opcodes/new-array.sbox b/vm/src/test/resources/opcodes/new-array.sbox index 1c4e6e15..af764a3f 100644 --- a/vm/src/test/resources/opcodes/new-array.sbox +++ b/vm/src/test/resources/opcodes/new-array.sbox @@ -1,4 +1,5 @@ -watts-limit: 1000 +vm: + watts-limit: 1000 code: |- new int8[1,2,3] --- diff --git a/vm/src/test/resources/opcodes/new-data-with-ref.sbox b/vm/src/test/resources/opcodes/new-data-with-ref.sbox index 8470af53..074ec0ff 100644 --- a/vm/src/test/resources/opcodes/new-data-with-ref.sbox +++ b/vm/src/test/resources/opcodes/new-data-with-ref.sbox @@ -1,4 +1,5 @@ -watts-limit: 1000 +vm: + watts-limit: 1000 code: |- new "boxed" new { "hello": #0 } diff --git a/vm/src/test/resources/opcodes/new-ref-array.sbox b/vm/src/test/resources/opcodes/new-ref-array.sbox index 6b2bb174..7c548897 100644 --- a/vm/src/test/resources/opcodes/new-ref-array.sbox +++ b/vm/src/test/resources/opcodes/new-ref-array.sbox @@ -1,4 +1,5 @@ -watts-limit: 1000 +vm: + watts-limit: 1000 code: |- new #[0] --- diff --git a/vm/src/test/resources/opcodes/new-ref.sbox b/vm/src/test/resources/opcodes/new-ref.sbox index 0cc16b3d..6dc10f70 100644 --- a/vm/src/test/resources/opcodes/new-ref.sbox +++ b/vm/src/test/resources/opcodes/new-ref.sbox @@ -1,4 +1,5 @@ -watts-limit: 1000 +vm: + watts-limit: 1000 code: |- new #42 --- diff --git a/vm/src/test/resources/opcodes/new.sbox b/vm/src/test/resources/opcodes/new.sbox index b6eff382..be21f63e 100644 --- a/vm/src/test/resources/opcodes/new.sbox +++ b/vm/src/test/resources/opcodes/new.sbox @@ -1,4 +1,5 @@ -watts-limit: 1000 +vm: + watts-limit: 1000 code: |- new { "hello": "world" } --- diff --git a/vm/src/test/resources/opcodes/push-ref.sbox b/vm/src/test/resources/opcodes/push-ref.sbox index 8debf10d..fc22d025 100644 --- a/vm/src/test/resources/opcodes/push-ref.sbox +++ b/vm/src/test/resources/opcodes/push-ref.sbox @@ -1,4 +1,5 @@ -watts-limit: 1000 +vm: + watts-limit: 1000 code: |- push #10 --- diff --git a/vm/src/test/resources/opcodes/push.sbox b/vm/src/test/resources/opcodes/push.sbox index 630e76d3..e01c8ef9 100644 --- a/vm/src/test/resources/opcodes/push.sbox +++ b/vm/src/test/resources/opcodes/push.sbox @@ -1,4 +1,5 @@ -watts-limit: 1000 +vm: + watts-limit: 1000 code: |- push int8(10) --- diff --git a/vm/src/test/resources/opcodes/sput-sget-flat-struct.sbox b/vm/src/test/resources/opcodes/sput-sget-flat-struct.sbox new file mode 100644 index 00000000..b8ec6164 --- /dev/null +++ b/vm/src/test/resources/opcodes/sput-sget-flat-struct.sbox @@ -0,0 +1,32 @@ +vm: + watts-limit: 1000 +code: |- + new { "hello": 42, "world": 7 } + push "key" + sput + push "key" + sget +--- +watts-spent: 345 +stack: + - ref.1 +heap: + ref.0: + utf8.world: int8.7 + utf8.hello: int8.42 + ref.1: + utf8.world: int8.7 + utf8.hello: int8.42 +effects: + - eventType: StorageWrite + program: "0000000000000000000000000000000000000000000000000000000000000000" + key: utf8.key + value: + utf8.hello: int8.42 + utf8.world: int8.7 + - eventType: StorageRead + program: "0000000000000000000000000000000000000000000000000000000000000000" + key: utf8.key + value: + utf8.hello: int8.42 + utf8.world: int8.7 diff --git a/vm/src/test/resources/opcodes/sput-sget-non-flat-struct.sbox b/vm/src/test/resources/opcodes/sput-sget-non-flat-struct.sbox new file mode 100644 index 00000000..db05374e --- /dev/null +++ b/vm/src/test/resources/opcodes/sput-sget-non-flat-struct.sbox @@ -0,0 +1,22 @@ +vm: + watts-limit: 1000 +code: |- + new { "hello": 42 } + dup + new { "world": 7 } + struct_mut "r" + push "key" + sput + push "key" + sget +--- +watts-spent: 106 +heap: + ref.0: + utf8.hello: int8.42 + utf8.r: ref.1 + ref.1: + utf8.world: int8.7 +error: + code: 104 + message: WrongType \ No newline at end of file diff --git a/vm/src/test/resources/opcodes/sput-sget-primitive.sbox b/vm/src/test/resources/opcodes/sput-sget-primitive.sbox new file mode 100644 index 00000000..cdd91bf3 --- /dev/null +++ b/vm/src/test/resources/opcodes/sput-sget-primitive.sbox @@ -0,0 +1,21 @@ +vm: + watts-limit: 1000 +code: |- + push "value" + push "key" + sput + push "key" + sget +--- +watts-spent: 241 +stack: + - utf8.value +effects: + - eventType: StorageWrite + program: "0000000000000000000000000000000000000000000000000000000000000000" + key: utf8.key + value: utf8.value + - eventType: StorageRead + program: "0000000000000000000000000000000000000000000000000000000000000000" + key: utf8.key + value: utf8.value diff --git a/vm/src/test/resources/opcodes/transfer.sbox b/vm/src/test/resources/opcodes/transfer.sbox index f25a159d..28c1c4c1 100644 --- a/vm/src/test/resources/opcodes/transfer.sbox +++ b/vm/src/test/resources/opcodes/transfer.sbox @@ -1,21 +1,22 @@ -watts-limit: 1000 -executor: 25a0e8fe9e30d91c4f5a5ef3dbb405ac65176901ae453771b27c3c83c714587d -balances: - 25a0e8fe9e30d91c4f5a5ef3dbb405ac65176901ae453771b27c3c83c714587d: bigint.1000000 - a4b6e44ddd94b7ceee9b38c5e1f05c9509688288b8328e63a4cdc7defeaa2a7b: bigint.1199 +vm: + watts-limit: 1000 + executor: 25a0e8fe9e30d91c4f5a5ef3dbb405ac65176901ae453771b27c3c83c714587d + balances: + 25a0e8fe9e30d91c4f5a5ef3dbb405ac65176901ae453771b27c3c83c714587d: int64.1000000 + a4b6e44ddd94b7ceee9b38c5e1f05c9509688288b8328e63a4cdc7defeaa2a7b: int64.1199 code: |- push xa4b6e44ddd94b7ceee9b38c5e1f05c9509688288b8328e63a4cdc7defeaa2a7b - push bigint(100000) + push int64(100000) transfer push xa4b6e44ddd94b7ceee9b38c5e1f05c9509688288b8328e63a4cdc7defeaa2a7b balance --- watts-spent: 145 stack: - - bigint.101199 + - int64.101199 effects: - eventType: Transfer - from: 0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 + from: 25a0e8fe9e30d91c4f5a5ef3dbb405ac65176901ae453771b27c3c83c714587d to: a4b6e44ddd94b7ceee9b38c5e1f05c9509688288b8328e63a4cdc7defeaa2a7b amount: 100000 - eventType: ShowBalance diff --git a/vm/src/test/resources/stdlib/scall-bytestohex.sbox b/vm/src/test/resources/stdlib/scall-bytestohex.sbox new file mode 100644 index 00000000..4b11c5a5 --- /dev/null +++ b/vm/src/test/resources/stdlib/scall-bytestohex.sbox @@ -0,0 +1,11 @@ +vm: + watts-limit: 1000 + stack: + - bytes.0000000000000000000000000000000000000000000000000000000000000000 + - int32.5 +code: |- + scall +--- +watts-spent: 431 +stack: + - utf8.0000000000000000000000000000000000000000000000000000000000000000 diff --git a/vm/src/test/resources/stdlib/scall-ed25519-verify-false.sbox b/vm/src/test/resources/stdlib/scall-ed25519-verify-false.sbox index dcb4cf90..cb916aaa 100644 --- a/vm/src/test/resources/stdlib/scall-ed25519-verify-false.sbox +++ b/vm/src/test/resources/stdlib/scall-ed25519-verify-false.sbox @@ -1,9 +1,10 @@ -watts-limit: 3000 -stack: - - bytes.5877667B812E22A18CF4555D0E5982E5584606214C26B47C967A19C9720E67E3 - - utf8.hello world - - bytes.D1C4F938E388EA5D4F19B939ABD4B0B2DDA3D8F447A34E1B782E36BDC8C65E6368E712166907396C37F6C0B6E28E6F61AD36933142D0B1AB69E63EC9F4300104 - - int32.1 +vm: + watts-limit: 3000 + stack: + - bytes.5877667B812E22A18CF4555D0E5982E5584606214C26B47C967A19C9720E67E3 + - utf8.hello world + - bytes.D1C4F938E388EA5D4F19B939ABD4B0B2DDA3D8F447A34E1B782E36BDC8C65E6368E712166907396C37F6C0B6E28E6F61AD36933142D0B1AB69E63EC9F4300104 + - int32.1 code: |- scall --- diff --git a/vm/src/test/resources/stdlib/scall-ed25519-verify.sbox b/vm/src/test/resources/stdlib/scall-ed25519-verify.sbox index 4da3a399..09562d57 100644 --- a/vm/src/test/resources/stdlib/scall-ed25519-verify.sbox +++ b/vm/src/test/resources/stdlib/scall-ed25519-verify.sbox @@ -1,9 +1,10 @@ -watts-limit: 3000 -stack: - - bytes.5877667B812E22A18CF4555D0E5982E5584606214C26B47C967A19C9720E67E3 - - utf8.hello world - - bytes.C9C4F938E388EA5D4F19B939ABD4B0B2DDA3D8F447A34E1B782E36BDC8C65E6368E712166907396C37F6C0B6E28E6F61AD36933142D0B1AB69E63EC9F4300104 - - int32.1 +vm: + watts-limit: 3000 + stack: + - bytes.5877667B812E22A18CF4555D0E5982E5584606214C26B47C967A19C9720E67E3 + - utf8.hello world + - bytes.C9C4F938E388EA5D4F19B939ABD4B0B2DDA3D8F447A34E1B782E36BDC8C65E6368E712166907396C37F6C0B6E28E6F61AD36933142D0B1AB69E63EC9F4300104 + - int32.1 code: |- scall --- diff --git a/vm/src/test/resources/stdlib/scall-exponent-bigint.sbox b/vm/src/test/resources/stdlib/scall-exponent-bigint.sbox index 3ac95201..3209c9e3 100644 --- a/vm/src/test/resources/stdlib/scall-exponent-bigint.sbox +++ b/vm/src/test/resources/stdlib/scall-exponent-bigint.sbox @@ -1,8 +1,9 @@ -watts-limit: 1000 +vm: + watts-limit: 1000 code: |- push bigint(54) push bigint(543) - push 0x03 + push 3 scall --- watts-spent: 124 diff --git a/vm/src/test/resources/stdlib/scall-exponent-overflow.sbox b/vm/src/test/resources/stdlib/scall-exponent-overflow.sbox index 09000ab8..42090127 100644 --- a/vm/src/test/resources/stdlib/scall-exponent-overflow.sbox +++ b/vm/src/test/resources/stdlib/scall-exponent-overflow.sbox @@ -1,20 +1,21 @@ -watts-limit: 1000 +vm: + watts-limit: 1000 code: |- push number(1000000000000000.0) push number(546.0) - push 0x03 + push 3 scall push int32(1000000000) push int32(1000) - push 0x03 + push 3 scall - push number(10.0) + push int32(10) push bigint(1000) - push 0x03 + push 3 scall --- watts-spent: 172 stack: - number.Infinity - - int32.2147483647 + - int32.0 - bigint.1000000000000000000000000000000 diff --git a/vm/src/test/resources/stdlib/scall-exponent-smallint-overflow.sbox b/vm/src/test/resources/stdlib/scall-exponent-smallint-overflow.sbox index 79afb92d..2d6e5d28 100644 --- a/vm/src/test/resources/stdlib/scall-exponent-smallint-overflow.sbox +++ b/vm/src/test/resources/stdlib/scall-exponent-smallint-overflow.sbox @@ -1,35 +1,21 @@ -watts-limit: 1000 +vm: + watts-limit: 1000 code: |- - push bigint(1000000000005) - push 0x2 - push 0x03 + push int32(10) + push int8(2) + push 3 scall - push bigint(10) - push int8(0x2) - push 0x03 - scall - push bigint(100000000000) - push int8(0x2) - push 0x03 - scall - push bigint(1000000000000000000000000000000000000000) - push int32(0x2) - push 0x03 - scall - push bigint(1000000000000000000000000000000000000) - push int32(0x2) - push 0x03 - scall - eq - push bigint(1000000) + push int32(1000000) push int16(2) - push 0x03 + push 3 + scall + push int32(1000000000) + push int32(2) + push 3 scall --- -watts-spent: 250 +watts-spent: 172 stack: - - uint8.32 - - int32.1024 - - int32.2147483647 - - bool.true - - int16.-1 + - int8.0 + - int16.0 + - int32.0 diff --git a/vm/src/test/resources/stdlib/scall-exponent.sbox b/vm/src/test/resources/stdlib/scall-exponent.sbox index eceb001a..0b56c5f6 100644 --- a/vm/src/test/resources/stdlib/scall-exponent.sbox +++ b/vm/src/test/resources/stdlib/scall-exponent.sbox @@ -1,10 +1,11 @@ -watts-limit: 1000 +vm: + watts-limit: 1000 code: |- - push 0x02 - push 0x02 - push 0x03 + push 2 + push 2 + push 3 scall --- watts-spent: 124 stack: - - uint8.4 + - int8.4 diff --git a/vm/src/test/resources/stdlib/scall-ripemd160.sbox b/vm/src/test/resources/stdlib/scall-ripemd160.sbox index e6334df7..fc528ac4 100644 --- a/vm/src/test/resources/stdlib/scall-ripemd160.sbox +++ b/vm/src/test/resources/stdlib/scall-ripemd160.sbox @@ -1,4 +1,5 @@ -watts-limit: 1000 +vm: + watts-limit: 1000 code: |- push "hello world" push 0x02 diff --git a/vm/src/test/scala/pravda/vm/VmSandbox.scala b/vm/src/test/scala/pravda/vm/VmSandbox.scala index 62c50113..4f973720 100644 --- a/vm/src/test/scala/pravda/vm/VmSandbox.scala +++ b/vm/src/test/scala/pravda/vm/VmSandbox.scala @@ -1,18 +1,25 @@ package pravda.vm + import com.google.protobuf.ByteString +import pravda.common.bytes import pravda.common.domain.{Address, NativeCoin} import pravda.vm import pravda.vm.Data.Primitive +import pravda.vm.Error.DataError +import pravda.vm.impl.{MemoryImpl, VmImpl, WattCounterImpl} import scala.collection.mutable +import scala.collection.mutable.ArrayBuffer +import scala.util.Try object VmSandbox { class EnvironmentSandbox(effects: mutable.Buffer[vm.Effect], initStorages: Map[Address, Map[Primitive, Data]], - initBalances: Seq[(Address, Primitive.BigInt)], + initBalances: Seq[(Address, Primitive.Int64)], initPrograms: Seq[(Address, Primitive.Bytes)], - pExecutor: Address) + pExecutor: Address, + appStateInfo: AppStateInfo) extends Environment { private val balances = mutable.Map(initBalances: _*) @@ -47,21 +54,24 @@ object VmSandbox { } def balance(address: Address): NativeCoin = { - val balance = NativeCoin @@ balances.get(address).fold(0L)(_.data.toLong) + val balance = NativeCoin @@ balances.get(address).fold(0L)(_.data) effects += vm.Effect.ShowBalance(address, balance) balance } def transfer(from: Address, to: Address, amount: NativeCoin): Unit = { - val fromBalance = balances.get(from).fold(scala.BigInt(0))(_.data) - val toBalance = balances.get(to).fold(scala.BigInt(0))(_.data) - balances(from) = Data.Primitive.BigInt(fromBalance - amount) - balances(to) = Data.Primitive.BigInt(toBalance + amount) + val fromBalance = balances.get(from).fold(0L)(_.data) + val toBalance = balances.get(to).fold(0L)(_.data) + balances(from) = Data.Primitive.Int64(fromBalance - amount) + balances(to) = Data.Primitive.Int64(toBalance + amount) effects += vm.Effect.Transfer(from, to, amount) } def event(address: Address, name: String, data: MarshalledData): Unit = effects += vm.Effect.Event(address, name, data) + + def chainHeight = appStateInfo.height + def lastBlockHash = appStateInfo.`app-hash` } class StorageSandbox(address: Address, effects: mutable.Buffer[vm.Effect], initStorage: Seq[(Data.Primitive, Data)]) @@ -87,4 +97,125 @@ object VmSandbox { prev } } + + /** + * Preconditions for VM sandbox + * + * @param balances Balances of accounts + * @param stack Initial stack of VM + * @param heap Initial heap of VM + * @param storage Initial storage, the program is run on the account with zero address (0x00 32 times) + * @param `program-storage` Initial storages for given account addresses + * @param programs Bytecodes of programs with given addresses + * @param executor Address of executor of the program, default executor address is 0x010203...1E1F20 + * @param code Bytecode of the program + * @param `app-state-info` Various Blockchain info + */ + final case class Preconditions( + `watts-limit`: Long = 1000000L, + balances: Map[Address, Primitive.Int64] = Map.empty, + stack: Seq[Primitive] = Nil, + heap: Map[Primitive.Ref, Data] = Map.empty, + storage: Map[Primitive, Data] = Map.empty, + `program-storage`: Map[Address, Map[Primitive, Data]] = Map.empty, + programs: Map[Address, Primitive.Bytes] = Map.empty, + executor: Option[Address] = None, + `app-state-info`: AppStateInfo = AppStateInfo( + `app-hash` = bytes.hex2byteString("0000000000000000000000000000000000000000000000000000000000000000"), + height = 1L)) + + /** + * @param stack Expected stack of VM after program execution + * @param heap Expected heap of VM after program execution + * @param effects Expected effect occurred during program execution + * @param error Expected error occurred during program execution + */ + final case class Expectations(`watts-spent`: Long, + stack: Seq[Primitive] = Nil, + heap: Map[Primitive.Ref, Data] = Map.empty, + effects: Seq[vm.Effect] = Nil, + error: Option[vm.Error] = None) + + final case class ExpectationsWithoutWatts(stack: Seq[Primitive] = Nil, + heap: Map[Primitive.Ref, Data] = Map.empty, + effects: Seq[vm.Effect] = Nil, + error: Option[vm.Error] = None) + + object ExpectationsWithoutWatts { + + def fromExpectations(e: Expectations): ExpectationsWithoutWatts = + ExpectationsWithoutWatts(e.stack, e.heap, e.effects, e.error) + } + + def run(input: VmSandbox.Preconditions, code: ByteString): VmSandbox.Expectations = { + val sandboxVm = new VmImpl() + val heap = { + if (input.heap.nonEmpty) { + val length = input.heap.map(_._1.data).max + 1 + val buffer = ArrayBuffer.fill[Data](length)(Data.Primitive.Null) + input.heap.foreach { case (ref, value) => buffer(ref.data) = value } + buffer + } else { + ArrayBuffer[Data]() + } + } + val memory = MemoryImpl(ArrayBuffer(input.stack: _*), heap) + val wattCounter = new WattCounterImpl(input.`watts-limit`) + + val pExecutor = input.executor.getOrElse { + Address @@ ByteString.copyFrom((1 to 32).map(_.toByte).toArray) + } + + val effects = mutable.Buffer[vm.Effect]() + val environment: Environment = new VmSandbox.EnvironmentSandbox( + effects, + input.`program-storage`, + input.balances.toSeq, + input.programs.toSeq, + pExecutor, + input.`app-state-info` + ) + val storage = new VmSandbox.StorageSandbox(Address.Void, effects, input.storage.toSeq) + + val error = Try { + memory.enterProgram(Address.Void) + sandboxVm.runBytes( + code.asReadOnlyByteBuffer(), + environment, + memory, + wattCounter, + Some(storage), + Some(Address.Void), + pcallAllowed = true + ) + memory.exitProgram() + }.fold( + { + case e: Data.DataException => + Some( + RuntimeException( + DataError(e.getMessage), + FinalState(wattCounter.spent, wattCounter.refund, wattCounter.total, memory.stack, memory.heap), + memory.callStack, + memory.currentCounter + )) + case ThrowableVmError(e) => + Some( + RuntimeException( + e, + FinalState(wattCounter.spent, wattCounter.refund, wattCounter.total, memory.stack, memory.heap), + memory.callStack, + memory.currentCounter)) + }, + _ => None + ) + + Expectations( + wattCounter.spent, + memory.stack, + memory.heap.zipWithIndex.map { case (d, i) => Data.Primitive.Ref(i) -> d }.toMap, + effects, + error.map(_.error) + ) + } } diff --git a/vm/src/test/scala/pravda/vm/VmSuite.scala b/vm/src/test/scala/pravda/vm/VmSuite.scala index dd9f5152..02a6ffb4 100644 --- a/vm/src/test/scala/pravda/vm/VmSuite.scala +++ b/vm/src/test/scala/pravda/vm/VmSuite.scala @@ -4,129 +4,37 @@ import java.io.File import com.google.protobuf.ByteString import org.json4s.DefaultFormats -import pravda.common.domain.Address import pravda.common.json._ import pravda.plaintest._ import pravda.vm import pravda.vm.Data.Primitive -import pravda.vm.Error.DataError import pravda.vm.asm.PravdaAssembler -import pravda.vm.impl.{MemoryImpl, VmImpl, WattCounterImpl} import pravda.vm.json._ -import scala.collection.mutable -import scala.collection.mutable.ArrayBuffer -import scala.util.Try - object VmSuiteData { - - final case class Preconditions(`watts-limit`: Long = 0, - balances: Map[Address, Primitive.BigInt] = Map.empty, - stack: Seq[Primitive] = Nil, - heap: Map[Primitive.Ref, Data] = Map.empty, - storage: Map[Primitive, Data] = Map.empty, - `program-storage`: Map[Address, Map[Primitive, Data]] = Map.empty, - programs: Map[Address, Primitive.Bytes] = Map.empty, - executor: Option[Address] = None, - code: String) - - final case class Expectations(`watts-spent`: Long, - stack: Seq[Primitive] = Nil, - heap: Map[Primitive.Ref, Data] = Map.empty, - effects: Seq[vm.Effect] = Nil, - error: Option[vm.Error] = None) + final case class Preconditions(vm: VmSandbox.Preconditions, code: String) } import pravda.vm.VmSuiteData._ -object VmSuite extends Plaintest[Preconditions, Expectations] { +object VmSuite extends Plaintest[Preconditions, VmSandbox.Expectations] { lazy val dir = new File("vm/src/test/resources") override lazy val ext = "sbox" override lazy val formats = DefaultFormats + json4sFormat[Data] + json4sFormat[Primitive] + - json4sFormat[Primitive.BigInt] + + json4sFormat[Primitive.Int64] + json4sFormat[Primitive.Bytes] + + json4sFormat[ByteString] + json4sFormat[vm.Effect] + json4sFormat[vm.Error] + json4sKeyFormat[ByteString] + json4sKeyFormat[Primitive.Ref] + json4sKeyFormat[Primitive] - override def produce(input: Preconditions): Either[String, Expectations] = { - val sandboxVm = new VmImpl() - val asmProgramE = PravdaAssembler.assemble(input.code, saveLabels = true) - val heap = { - if (input.heap.nonEmpty) { - val length = input.heap.map(_._1.data).max + 1 - val buffer = ArrayBuffer.fill[Data](length)(Data.Primitive.Null) - input.heap.foreach { case (ref, value) => buffer(ref.data) = value } - buffer - } else { - ArrayBuffer[Data]() - } - } - val memory = MemoryImpl(ArrayBuffer(input.stack: _*), heap) - val wattCounter = new WattCounterImpl(input.`watts-limit`) - - val pExecutor = input.executor.getOrElse { - Address @@ ByteString.copyFrom((1 to 32).map(_.toByte).toArray) - } - - val effects = mutable.Buffer[vm.Effect]() - val environment: Environment = new VmSandbox.EnvironmentSandbox( - effects, - input.`program-storage`, - input.balances.toSeq, - input.programs.toSeq, - pExecutor - ) - val storage = new VmSandbox.StorageSandbox(Address.Void, effects, input.storage.toSeq) - + override def produce(input: Preconditions): Either[String, VmSandbox.Expectations] = for { - asmProgram <- asmProgramE.left.map(_.mkString) - } yield { - val error = Try { - memory.enterProgram(Address.Void) - sandboxVm.runBytes( - asmProgram.asReadOnlyByteBuffer(), - environment, - memory, - wattCounter, - Some(storage), - Some(Address.Void), - pcallAllowed = true - ) - memory.exitProgram() - }.fold( - { - case e: Data.DataException => - Some( - RuntimeException( - DataError(e.getMessage), - FinalState(wattCounter.spent, wattCounter.refund, wattCounter.total, memory.stack, memory.heap), - memory.callStack, - memory.currentCounter - )) - case ThrowableVmError(e) => - Some( - RuntimeException( - e, - FinalState(wattCounter.spent, wattCounter.refund, wattCounter.total, memory.stack, memory.heap), - memory.callStack, - memory.currentCounter)) - }, - _ => None - ) - - Expectations( - wattCounter.spent, - memory.stack, - memory.heap.zipWithIndex.map { case (d, i) => Data.Primitive.Ref(i) -> d }.toMap, - effects, - error.map(_.error) - ) - } - } + asm <- PravdaAssembler.assemble(input.code, saveLabels = true).left.map(_.mkString) + } yield VmSandbox.run(input.vm, asm) } diff --git a/yaml4s/src/main/scala/pravda/yaml4s/YamlMethods.scala b/yaml4s/src/main/scala/pravda/yaml4s/YamlMethods.scala index 55d7495a..ccec50ad 100644 --- a/yaml4s/src/main/scala/pravda/yaml4s/YamlMethods.scala +++ b/yaml4s/src/main/scala/pravda/yaml4s/YamlMethods.scala @@ -112,6 +112,91 @@ object YamlMethods { } } + /** + * Render diff of two JValues as yaml. + * Changed parts are coloured yellow, added parts -- green, deleted parts -- red. + * The style is fixed, and produced yaml can be wrong, it should be used only in demonstrating purposes. + * + * @param node Obtained JValue. + * @param expected Expected JValue. + * @return Yaml with colours. + */ + def renderDiff(node: JValue, expected: JValue): String = { + + def print(j: JValue) = yaml.dump(jvalue2java(j)).trim + def withColour(s: String, colour: String) = s"$colour$s${Console.RESET}" + def printWithColour(j: JValue, colour: String) = withColour(print(j), colour) + + def withNewLine(s: String) = if (s.nonEmpty) "\n" + s else s + + def diff(j1: JValue, j2: JValue): String = (j1, j2) match { + case (x, y) if x == y => print(x) + case (JObject(xs), JObject(ys)) => diffFields(xs, ys) + case (JArray(xs), JArray(ys)) => diffVals(xs, ys) + case (x: JInt, y: JInt) if x != y => printWithColour(y, Console.YELLOW) + case (x: JDouble, y: JDouble) if x != y => printWithColour(y, Console.YELLOW) + case (x: JDecimal, y: JDecimal) if x != y => printWithColour(y, Console.YELLOW) + case (x: JString, y: JString) if x != y => printWithColour(y, Console.YELLOW) + case (x: JBool, y: JBool) if x != y => printWithColour(y, Console.YELLOW) + case (JNothing, x) => printWithColour(x, Console.GREEN) + case (x, JNothing) => printWithColour(x, Console.RED) + case (x, y) => printWithColour(y, Console.YELLOW) + } + + def diffFields(xs: List[JField], ys: List[JField]): String = { + def formatElem(name: String, elem: String) = { + val lines = elem.lines.toList + if (lines.length <= 1) { + s"$name: $elem" + } else { + s"""$name: + | ${lines.mkString("\n ")} + """.stripMargin + } + } + + xs match { + case (xname, xvalue) :: xtail if ys.exists(_._1 == xname) => + val (_, yvalue) = ys.find(_._1 == xname).get + formatElem(xname, diff(xvalue, yvalue)) + withNewLine(diffFields(xtail, ys.filterNot(_ == xname -> yvalue))) + case (xname, xvalue) :: xtail => + withColour(formatElem(xname, print(xvalue)), Console.RED) + withNewLine(diffFields(xtail, ys)) + case Nil => + ys match { + case (yname, yvalue) :: ytail => + withColour(formatElem(yname, print(yvalue)), Console.GREEN) + withNewLine(diffFields(Nil, ytail)) + case Nil => "" + } + } + } + + def diffVals(xs: List[JValue], ys: List[JValue]): String = { + def formatElem(elem: String) = { + val lines = elem.lines.toList + lines match { + case Nil => s"-" + case head :: tail => + val h = s"- $head" + val t = if (tail.nonEmpty) { + s"\n${tail.map(s => s" $s").mkString("\n")}" + } else { + "" + } + h + t + } + } + + (xs, ys) match { + case (x :: xtail, y :: ytail) => formatElem(diff(x, y)) + withNewLine(diffVals(xtail, ytail)) + case (Nil, y :: ytail) => withColour(formatElem(print(y)), Console.GREEN) + withNewLine(diffVals(Nil, ytail)) + case (x :: xtail, Nil) => withColour(formatElem(print(x)), Console.RED) + withNewLine(diffVals(xtail, Nil)) + case (Nil, Nil) => "" + } + } + + diff(node, expected) + } + def parseOpt(in: JsonInput, useBigDecimalForDouble: Boolean): Option[JValue] = Try { parseUnsafe(in, useBigDecimalForDouble) }.toOption diff --git a/yaml4s/src/main/scala/pravda/yaml4s/package.scala b/yaml4s/src/main/scala/pravda/yaml4s/package.scala index ce79da16..bbb7612c 100644 --- a/yaml4s/src/main/scala/pravda/yaml4s/package.scala +++ b/yaml4s/src/main/scala/pravda/yaml4s/package.scala @@ -22,6 +22,8 @@ package object yaml4s { def renderYaml(node: JValue): String = YamlMethods.render(node) + def renderYamlDiff(node: JValue, expected: JValue): String = YamlMethods.renderDiff(node, expected) + def parseYamlOpt(in: JsonInput, useBigDecimalForDouble: Boolean): Option[JValue] = YamlMethods.parseOpt(in, useBigDecimalForDouble) diff --git a/yaml4s/src/test/scala/pravda/yaml4s/YamlDiffSuite.scala b/yaml4s/src/test/scala/pravda/yaml4s/YamlDiffSuite.scala new file mode 100644 index 00000000..eabf9d02 --- /dev/null +++ b/yaml4s/src/test/scala/pravda/yaml4s/YamlDiffSuite.scala @@ -0,0 +1,150 @@ +package pravda.yaml4s + +import org.json4s._ +import org.json4s.native.JsonMethods._ +import utest._ + +import scala.io.AnsiColor + +object YamlDiffSuite extends TestSuite { + + private val coloursLabels = Map( + AnsiColor.BLACK -> "black", + AnsiColor.RED -> "red", + AnsiColor.GREEN -> "green", + AnsiColor.YELLOW -> "yellow", + AnsiColor.BLUE -> "blue", + AnsiColor.MAGENTA -> "magenta", + AnsiColor.CYAN -> "cyan", + AnsiColor.WHITE -> "white", + AnsiColor.BLACK_B -> "black_b", + AnsiColor.RED_B -> "red_b", + AnsiColor.GREEN_B -> "green_b", + AnsiColor.YELLOW_B -> "yellow_b", + AnsiColor.BLUE_B -> "blue_b", + AnsiColor.MAGENTA_B -> "magenta_b", + AnsiColor.CYAN_B -> "cyan_b", + AnsiColor.WHITE_B -> "white_b", + AnsiColor.RESET -> "reset", + AnsiColor.BOLD -> "bold", + AnsiColor.UNDERLINED -> "underlined", + AnsiColor.BLINK -> "blink", + AnsiColor.REVERSED -> "reversed", + AnsiColor.INVISIBLE -> "invisible" + ) + + private def escapeColours(s: String): String = { + var res: String = s + for { + (colour, label) <- coloursLabels + } { + res = res.replace(colour, s"[$label]") + } + + res + } + + def tests = Tests { + "simple object diff" - { + val json1 = parse(""" + { + "first body": 1, + "second body": 2, + "third body": 3 + } + """) + val json2 = parse(""" + { + "first body": "one", + "second body": "two", + "third body": "three" + } + """) + + escapeColours(YamlMethods.renderDiff(json1, json2)) ==> + """first body: [yellow]one[reset] + |second body: [yellow]two[reset] + |third body: [yellow]three[reset]""".stripMargin + } + + "simple object deletion diff" - { + val json1 = parse(""" + { + "first body": 1, + "second body": "two", + "third body": "three", + "forth body": "four" + } + """) + val json2 = parse(""" + { + "first body": "one", + "second body": "two", + "third body": "three" + } + """) + + escapeColours(YamlMethods.renderDiff(json1, json2)) ==> + """first body: [yellow]one[reset] + |second body: two + |third body: three + |[red]forth body: four[reset]""".stripMargin + } + + "simple object addition diff" - { + val json1 = parse(""" + { + "first body": 1, + "second body": "two", + "third body": "three", + } + """) + val json2 = parse(""" + { + "first body": "one", + "second body": "two", + "third body": "three", + "forth body": "four" + } + """) + + escapeColours(YamlMethods.renderDiff(json1, json2)) ==> + """first body: [yellow]one[reset] + |second body: two + |third body: three + |[green]forth body: four[reset]""".stripMargin + } + + "simple object permutation diff" - { + val json1 = parse(""" + { + "second body": 2, + "first body": 1, + "third body": 3 + } + """) + val json2 = parse(""" + { + "first body": "one", + "second body": "two", + "third body": "three" + } + """) + + escapeColours(YamlMethods.renderDiff(json1, json2)) ==> + """second body: [yellow]two[reset] + |first body: [yellow]one[reset] + |third body: [yellow]three[reset]""".stripMargin + } + + "simple array diff" - { + val json1 = parse("[1, 2, 3]") + val json2 = parse("[\"one\", \"two\", \"three\"]") + + escapeColours(YamlMethods.renderDiff(json1, json2)) ==> + """- [yellow]one[reset] + |- [yellow]two[reset] + |- [yellow]three[reset]""".stripMargin + } + } +}