Merge branch 'master' into zomp

This commit is contained in:
Craig Everett 2024-11-01 13:58:32 +09:00
commit 9f90396023
49 changed files with 2390 additions and 1368 deletions

View File

@ -2,4 +2,4 @@ mkdocs==1.4.2
mkdocs-simple-hooks==0.1.5 mkdocs-simple-hooks==0.1.5
mkdocs-material==9.0.9 mkdocs-material==9.0.9
mike==1.1.2 mike==1.1.2
pygments==2.14.0 pygments==2.17.2

1
.gitignore vendored
View File

@ -24,3 +24,4 @@ current_counterexample.eqc
test/contracts/test.aes test/contracts/test.aes
__pycache__ __pycache__
.docssite/docs/*.md .docssite/docs/*.md
.vscode

View File

@ -6,6 +6,73 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased] ## [Unreleased]
### Added ### Added
- Added a check for number of type variables in a type signature; it is serialized using 8 bits,
so the upper limit is 256.
### Changed
### Removed
## [8.0.1]
### Changed
- Upgrade aebytecode to v3.4.1 to fix C warnings
## [8.0.0]
### Added
- Bitwise operations for integers: `band`, `bor`, `bxor`, `bnot`, `<<` and `>>`.
- `Int.mulmod` - combined builtin operation for multiplication and modulus.
- `Crypto.poseidon` - a ZK/SNARK-friendly hash function (over the BLS12-381 scalar field).
- `Address.to_bytes` - convert an address to its binary representation (for hashing, etc.).
- Raw data pointers added to AENS. In short we have introduced a new namespace
`AENSv2`; they contain types similar to the old `AENS`; `AENS.name` and
`AENS.pointee`, where the latter now has a constructor `DataPt(bytes())`. All
AENS actions have been moved to `AENSv2`, and `AENSv2.lookup` and
`AENSv2.update` consume and produce the new types. The old `AENS` namespace
only contains the old datatypes, that can be used to interface existing
contracts. Standard library `AENSCompat` is added to convert between old and
new pointers.
- Introduce arbitrary sized binary arrays (type `bytes()`); adding `Bytes.split_any`,
`Bytes.to_fixed_size`, `Bytes.to_any_size`, `Bytes.size`, `String.to_bytes`,
and `Int.to_bytes`; and adjust `Bytes.concat` to allow both fixed and arbitrary
sized byte arrays.
- `Chain.network_id` - a function to get hold of the Chain's network id.
- Allowing `Bytes.to_any_size` in calldata creation, to enable creation of arguments
with arbitray size.
- Signature literals `sg_...` - they have type `signature` (which is an alias for `bytes(64)`).
- Support for OTP-27 - no changes in behavior.
### Changed
- `Crypto.verify_sig` is changed to have `msg : bytes()`. I.e. the
signed data can be of any length (used to be limited to `bytes(32)`/`hash`).
- System aliases are handled explicitly when converting to a Sophia value, this is only
observable for `signature` where a value of type `signature` is now represented as a
(new) signature literal.
- Allow self-qualification, i.e. referencing `X.foo` when in namespace `X`.
### Removed
- `Bitwise.aes` standard library is removed - the builtin operations are superior.
## [7.4.1]
### Changed
- Improve how includes with relative paths are resolved during parsing/compilation. Relative
include paths are now always relative to the file containing the `include` statement.
### Fixed
- Disable unused type warnings for types used inside of records.
## [7.4.0]
### Changed
- Names of lifted lambdas now consist of parent function's name and their
position in the source code.
### Fixed
- Lifted lambdas get their names assigned deterministically.
## [7.3.0]
### Fixed
- Fixed a bug with polymorphism that allowed functions with the same name but different type to be considered as implementations for their corresponding interface function.
- Fixed a bug in the byte code optimization that incorrectly reordered dependent instructions.
## [7.2.1]
### Fixed
- Fixed bugs with the newly added debugging symbols
## [7.2.0]
### Added
- Toplevel compile-time constants - Toplevel compile-time constants
``` ```
namespace N = namespace N =
@ -13,8 +80,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
contract C = contract C =
let cc = 2 let cc = 2
``` ```
### Changed - API functions for encoding/decoding Sophia values to/from FATE.
### Removed ### Removed
- Remove the mapping from variables to FATE registers from the compilation output.
### Fixed ### Fixed
- Warning about unused include when there is no include. - Warning about unused include when there is no include.
@ -388,7 +456,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Simplify calldata creation - instead of passing a compiled contract, simply - Simplify calldata creation - instead of passing a compiled contract, simply
pass a (stubbed) contract string. pass a (stubbed) contract string.
[Unreleased]: https://github.com/aeternity/aesophia/compare/v7.1.0...HEAD [Unreleased]: https://github.com/aeternity/aesophia/compare/v8.0.1...HEAD
[8.0.1]: https://github.com/aeternity/aesophia/compare/v8.0.0...v8.0.1
[8.0.0]: https://github.com/aeternity/aesophia/compare/v7.4.1...v8.0.0
[7.4.1]: https://github.com/aeternity/aesophia/compare/v7.4.0...v7.4.1
[7.4.0]: https://github.com/aeternity/aesophia/compare/v7.3.0...v7.4.0
[7.3.0]: https://github.com/aeternity/aesophia/compare/v7.2.1...v7.3.0
[7.2.1]: https://github.com/aeternity/aesophia/compare/v7.2.0...v7.2.1
[7.2.0]: https://github.com/aeternity/aesophia/compare/v7.1.0...v7.2.0
[7.1.0]: https://github.com/aeternity/aesophia/compare/v7.0.1...v7.1.0 [7.1.0]: https://github.com/aeternity/aesophia/compare/v7.0.1...v7.1.0
[7.0.1]: https://github.com/aeternity/aesophia/compare/v7.0.0...v7.0.1 [7.0.1]: https://github.com/aeternity/aesophia/compare/v7.0.0...v7.0.1
[7.0.0]: https://github.com/aeternity/aesophia/compare/v6.1.0...v7.0.0 [7.0.0]: https://github.com/aeternity/aesophia/compare/v6.1.0...v7.0.0

View File

@ -53,8 +53,6 @@ The **pp_** options all print to standard output the following:
The option `include_child_contract_symbols` includes the symbols of child contracts functions in the generated fate code. It is turned off by default to avoid making contracts bigger on chain. The option `include_child_contract_symbols` includes the symbols of child contracts functions in the generated fate code. It is turned off by default to avoid making contracts bigger on chain.
The option `debug_info` includes information related to debugging in the compiler output. Currently this option only includes the mapping from variables to registers.
#### Options to control which compiler optimizations should run: #### Options to control which compiler optimizations should run:
By default all optimizations are turned on, to disable an optimization, it should be By default all optimizations are turned on, to disable an optimization, it should be

View File

@ -84,7 +84,7 @@ the return value of the call.
```sophia ```sophia
contract interface VotingType = contract interface VotingType =
entrypoint : vote : string => unit entrypoint vote : string => unit
contract Voter = contract Voter =
entrypoint tryVote(v : VotingType, alt : string) = entrypoint tryVote(v : VotingType, alt : string) =
@ -204,7 +204,7 @@ When a `contract` or a `contract interface` implements another `contract interfa
#### Subtyping and variance #### Subtyping and variance
Subtyping in Sophia follows common rules that take type variance into account. As described by [Wikipedia](https://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)), Subtyping in Sophia follows common rules that take type variance into account. As described by [Wikipedia](https://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)),
>Variance refers to how subtyping between more complex types relates to subtyping between their components. >Variance refers to how subtyping between more complex types relates to subtyping between their components.
@ -224,11 +224,11 @@ A good example of where it matters can be pictured by subtyping of function type
```sophia ```sophia
contract interface Animal = contract interface Animal =
entrypoint age : () => int entrypoint age : () => int
contract Dog : Animal = contract Dog : Animal =
entrypoint age() = // ... entrypoint age() = // ...
entrypoint woof() = "woof" entrypoint woof() = "woof"
contract Cat : Animal = contract Cat : Animal =
entrypoint age() = // ... entrypoint age() = // ...
entrypoint meow() = "meow" entrypoint meow() = "meow"
@ -295,6 +295,11 @@ of `A`.
- When a user-defined type `t('a)` is invariant in `'a`, then `t(A)` can never be - When a user-defined type `t('a)` is invariant in `'a`, then `t(A)` can never be
a subtype of `t(B)`. a subtype of `t(B)`.
#### Type variable limitation
Because of how FATE represents types as values there is a fixed upper limit (256)
of type variables that can be used in a single type signature.
## Mutable state ## Mutable state
Sophia does not have arbitrary mutable state, but only a limited form of state Sophia does not have arbitrary mutable state, but only a limited form of state
@ -493,6 +498,24 @@ the file, except that error messages will refer to the original source
locations. The language will try to include each file at most one time automatically, locations. The language will try to include each file at most one time automatically,
so even cyclic includes should be working without any special tinkering. so even cyclic includes should be working without any special tinkering.
### Include files using relative paths
When including code from another file using the `include` statement, the path
is relative to _the file that includes it_. Consider the following file tree:
```
c1.aes
c3.aes
dir1/c2.aes
dir1/c3.aes
```
If `c1.aes` contains `include "c3.aes"` it will include the top level `c3.aes`,
while if `c2.aes` contained the same line it would as expected include
`dir1/c3.aes`.
Note: Prior to v7.5.0, it would consider the include path relative to _the main
contract file_ (or any explicitly set include path).
## Standard library ## Standard library
Sophia offers [standard library](sophia_stdlib.md) which exposes some Sophia offers [standard library](sophia_stdlib.md) which exposes some
@ -540,6 +563,7 @@ Sophia has the following types:
## Literals ## Literals
| Type | Constant/Literal example(s) | | Type | Constant/Literal example(s) |
| ---------- | ------------------------------- | | ---------- | ------------------------------- |
| unit | () |
| int | `-1`, `2425`, `4598275923475723498573485768` | | int | `-1`, `2425`, `4598275923475723498573485768` |
| address | `ak_2gx9MEFxKvY9vMG5YnqnXWv1hCsX7rgnfvBLJS4aQurustR1rt` | | address | `ak_2gx9MEFxKvY9vMG5YnqnXWv1hCsX7rgnfvBLJS4aQurustR1rt` |
| bool | `true`, `false` | | bool | `true`, `false` |
@ -554,7 +578,7 @@ Sophia has the following types:
| state | `state{ owner = Call.origin, magic_key = #a298105f }` | | state | `state{ owner = Call.origin, magic_key = #a298105f }` |
| event | `EventX(0, "Hello")` | | event | `EventX(0, "Hello")` |
| hash | `#000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f` | | hash | `#000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f` |
| signature | `#000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f` | | signature | `sg_MhibzTP1wWzGCTjtPFr1TiPqRJrrJqw7auvEuF5i3FdoALWqXLBDY6xxRRNUSPHK3EQTnTzF12EyspkxrSMxVHKsZeSMj`, `#000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f` |
| Chain.ttl | `FixedTTL(1050)`, `RelativeTTL(50)` | | Chain.ttl | `FixedTTL(1050)`, `RelativeTTL(50)` |
| oracle('a, 'b) | `ok_2YNyxd6TRJPNrTcEDCe9ra59SVUdp9FR9qWC5msKZWYD9bP9z5` | | oracle('a, 'b) | `ok_2YNyxd6TRJPNrTcEDCe9ra59SVUdp9FR9qWC5msKZWYD9bP9z5` |
| oracle_query('a, 'b) | `oq_2oRvyowJuJnEkxy58Ckkw77XfWJrmRgmGaLzhdqb67SKEL1gPY` | | oracle_query('a, 'b) | `oq_2oRvyowJuJnEkxy58Ckkw77XfWJrmRgmGaLzhdqb67SKEL1gPY` |
@ -610,14 +634,28 @@ arithmetic operations:
- remainder (`x mod y`), satisfying `y * (x / y) + x mod y == x` for non-zero `y` - remainder (`x mod y`), satisfying `y * (x / y) + x mod y == x` for non-zero `y`
- exponentiation (`x ^ y`) - exponentiation (`x ^ y`)
All operations are *safe* with respect to overflow and underflow. All operations are *safe* with respect to overflow and underflow.
The division and modulo operations throw an arithmetic error if the The division and modulo operations throw an arithmetic error if the
right-hand operand is zero. right-hand operand is zero.
Sophia arbitrary-sized integers (FATE) also supports the following bitwise operations:
- bitwise and (`x band y`)
- bitwise or (`x bor y`)
- bitwise xor (`x bxor y`)
- bitwise not (`bnot x`)
- arithmetic bitshift left (`x << n`)
- arithmetic bitshift right (`x >> n`)
Note: Arithmetic bitshift treats the number as a signed integer (in 2s
complement), and "retains" the topmost bit. I.e. shifting in zeros if the
topmost bit was 0, and ones if it was one.
## Bit fields ## Bit fields
Sophia integers do not support bit arithmetic. Instead there is a separate Originally Sophia integers did not support bit arithmetic. Instead we used a
type `bits`. See the standard library [documentation](sophia_stdlib.md#bits). separate type `bits` (see the standard library
[documentation](sophia_stdlib.md#bits)) - it is still provided as an
alternative to bit arithmetic.
A bit field can be of arbitrary size (but it is still represented by the A bit field can be of arbitrary size (but it is still represented by the
corresponding integer, so setting very high bits can be expensive). corresponding integer, so setting very high bits can be expensive).
@ -922,16 +960,18 @@ functions are provided.
## AENS interface ## AENS interface
Contracts can interact with the Contracts can interact with the [æternity naming
[æternity naming system](https://github.com/aeternity/protocol/blob/master/AENS.md). system](https://github.com/aeternity/protocol/blob/master/AENS.md). For this
For this purpose the [AENS](sophia_stdlib.md#aens) library was exposed. purpose the [AENS](sophia_stdlib.md#aens) and later the
[AENSv2](sophia_stdlib.md#aensv2) library was exposed.
### Example ### Example
In this example we assume that the name `name` already exists, and is owned by In this example we assume that the name `name` already exists, and is owned by
an account with address `addr`. In order to allow a contract `ct` to handle an account with address `addr`. In order to allow a contract `ct` to handle
`name` the account holder needs to create a `name` the account holder needs to create a [delegation
[signature](#delegation-signature) `sig` of `addr | name.hash | ct.address`. signature](#delegation-signature) `sig` from the name owner address `addr`, the
name hash and the contract address.
Armed with this information we can for example write a function that extends Armed with this information we can for example write a function that extends
the name if it expires within 1000 blocks: the name if it expires within 1000 blocks:
@ -1073,8 +1113,34 @@ however is in the gas consumption — while `abort` returns unused gas, a call t
## Delegation signature ## Delegation signature
Some chain operations (`Oracle.<operation>` and `AENS.<operation>`) have an Some chain operations (`Oracle.<operation>` and `AENSv2.<operation>`) have an
optional delegation signature. This is typically used when a user/accounts optional delegation signature. This is typically used when a user/accounts
would like to allow a contract to act on it's behalf. The exact data to be would like to allow a contract to act on it's behalf.
signed varies for the different operations, but in all cases you should prepend
the signature data with the `network_id` (`ae_mainnet` for the æternity mainnet, etc.). ### From Ceres
From the Ceres protocol version the delegation signatures have more structure,
including a unique tag, `network_id` and identifiers; there are five different
delegation signatures:
- AENS wildcard - the user signs: `owner account + contract`
- `AENS_PRECLAIM` - the user signs: `owner account + contract`
- `AENS_CLAIM, AENS_UPDATE, AENS_TRANSFER, AENS_REVOKE` - the user signs: `owner account + name hash + contract`
- `ORACLE_REGISTER, ORACLE_EXTEND` - the user signs: `owner account + contract`
- `ORACLE_RESPOND` - the user signs: `query id + contract`
See [Serialized signature
data](https://github.com/aeternity/protocol/blob/master/contracts/fate.md#from-ceres-serialized-signature-data)
for the exact structure used.
### Before ceres
The exact data to be signed varies for the different operations, but in all
cases you should prepend the signature data with the `network_id` (`ae_mainnet`
for the æternity mainnet, etc.).
There are four different delegation signatures:
- `AENS_PRECLAIM` - the user signs: owner `network_id + account + contract`
- `AENS_CLAIM, AENS_UPDATE, AENS_TRANSFER, AENS_REVOKE` - the user signs: `network_id + owner account + name hash + contract`
- `ORACLE_REGISTER, ORACLE_EXTEND` - the user signs: `network_id + owner account + contract`
- `ORACLE_RESPOND` - the user signs: `network_id + query id + contract`

View File

@ -14,6 +14,7 @@ The out-of-the-box namespaces are:
- [Address](#address) - [Address](#address)
- [AENS](#aens) - [AENS](#aens)
- [AENSv2](#aensv2)
- [Auth](#auth) - [Auth](#auth)
- [Bits](#bits) - [Bits](#bits)
- [Bytes](#bytes) - [Bytes](#bytes)
@ -31,6 +32,7 @@ The following ones need to be included as regular files with `.aes` suffix, for
include "List.aes" include "List.aes"
``` ```
- [AENSCompat](#aenscompat)
- [Bitwise](#bitwise) - [Bitwise](#bitwise)
- [BLS12_381](#bls12_381) - [BLS12_381](#bls12_381)
- [Func](#func) - [Func](#func)
@ -55,6 +57,12 @@ Address.to_str(a : address) : string
Base58 encoded string Base58 encoded string
#### to_bytes
```
Address.to_bytes(a : address) : bytes(32)
```
The binary representation of the address.
#### is_contract #### is_contract
``` ```
@ -90,13 +98,10 @@ Cast address to contract type C (where `C` is a contract)
### AENS ### AENS
The following functionality is available for interacting with the æternity The old AENS namespace, kept in the compiler to be able to interact with
naming system (AENS). contracts from before Ceres, compiled using aesophia compiler version 7.x and
If `owner` is equal to `Contract.address` the signature `signature` is earlier. Used in [AENSCompat](#aenscompat) when converting between old and new
ignored, and can be left out since it is a named argument. Otherwise we need pointers.
a signature to prove that we are allowed to do AENS operations on behalf of
`owner`. The [signature is tied to a network id](https://github.com/aeternity/protocol/blob/iris/consensus/consensus.md#transaction-signature),
i.e. the signature material should be prefixed by the network id.
#### Types #### Types
@ -113,12 +118,41 @@ datatype pointee = AccountPt(address) | OraclePt(address)
| ContractPt(address) | ChannelPt(address) | ContractPt(address) | ChannelPt(address)
``` ```
### AENSv2
Note: introduced in v8.0
The following functionality is available for interacting with the æternity
naming system (AENS). If `owner` is equal to `Contract.address` the signature
`signature` is ignored, and can be left out since it is a named argument.
Otherwise we need a signature to prove that we are allowed to do AENS
operations on behalf of `owner`. The [signature is tied to a network
id](https://github.com/aeternity/protocol/blob/iris/consensus/consensus.md#transaction-signature),
i.e. the signature material should be prefixed by the network id.
#### Types
##### name
```
datatype name = Name(address, Chain.ttl, map(string, AENSv2.pointee))
```
##### pointee
```
datatype pointee = AccountPt(address) | OraclePt(address)
| ContractPt(address) | ChannelPt(address) | DataPt(bytes())
```
Note: on-chain there is a maximum length enforced for `DataPt`, it is 1024 bytes.
Sophia itself does _not_ check for this.
#### Functions #### Functions
##### resolve ##### resolve
``` ```
AENS.resolve(name : string, key : string) : option('a) AENSv2.resolve(name : string, key : string) : option('a)
``` ```
Name resolution. Here `name` should be a registered name and `key` one of the attributes Name resolution. Here `name` should be a registered name and `key` one of the attributes
@ -129,74 +163,107 @@ type checked against this type at run time.
##### lookup ##### lookup
``` ```
AENS.lookup(name : string) : option(AENS.name) AENSv2.lookup(name : string) : option(AENSv2.name)
``` ```
If `name` is an active name `AENS.lookup` returns a name object. If `name` is an active name `AENSv2.lookup` returns a name object.
The three arguments to `Name` are `owner`, `expiry` and a map of the The three arguments to `Name` are `owner`, `expiry` and a map of the
`pointees` for the name. Note: the expiry of the name is always a fixed TTL. `pointees` for the name. Note: the expiry of the name is always a fixed TTL.
For example: For example:
``` ```
let Some(Name(owner, FixedTTL(expiry), ptrs)) = AENS.lookup("example.chain") let Some(AENSv2.Name(owner, FixedTTL(expiry), ptrs)) = AENSv2.lookup("example.chain")
``` ```
Note: Changed to produce `AENSv2.name` in v8.0 (Ceres protocol upgrade).
##### preclaim ##### preclaim
``` ```
AENS.preclaim(owner : address, commitment_hash : hash, <signature : signature>) : unit AENSv2.preclaim(owner : address, commitment_hash : hash, <signature : signature>) : unit
``` ```
The [signature](./sophia_features.md#delegation-signature) should be over The [signature](./sophia_features.md#delegation-signature) should be a
`network id` + `owner address` + `Contract.address` (concatenated as byte arrays). serialized structure containing `network id`, `owner address`, and
`Contract.address`.
From Ceres (i.e. FATE VM version 3) the
[signature](./sophia_features.md#delegation-signature) can also be generic
(allowing _all_, existing and future, names to be delegated with one
signature), i.e. containing `network id`, `owner address`, `Contract.address`.
##### claim ##### claim
``` ```
AENS.claim(owner : address, name : string, salt : int, name_fee : int, <signature : signature>) : unit AENSv2.claim(owner : address, name : string, salt : int, name_fee : int, <signature : signature>) : unit
``` ```
The [signature](./sophia_features.md#delegation-signature) should be over The [signature](./sophia_features.md#delegation-signature) should be a
`network id` + `owner address` + `name_hash` + `Contract.address` serialized structure containing `network id`, `owner address`, and
(concatenated as byte arrays) `Contract.address`. Using the private key of `owner address` for signing.
using the private key of the `owner` account for signing.
From Ceres (i.e. FATE VM version 3) the
[signature](./sophia_features.md#delegation-signature) can also be generic
(allowing _all_, existing and future, names to be delegated with one
signature), i.e. containing `network id`, `owner address`, `name_hash`, and
`Contract.address`.
##### transfer ##### transfer
``` ```
AENS.transfer(owner : address, new_owner : address, name : string, <signature : signature>) : unit AENSv2.transfer(owner : address, new_owner : address, name : string, <signature : signature>) : unit
``` ```
Transfers name to the new owner. Transfers name to the new owner.
The [signature](./sophia_features.md#delegation-signature) should be over The [signature](./sophia_features.md#delegation-signature) should be a
`network id` + `owner address` + `name_hash` + `Contract.address` serialized structure containing `network id`, `owner address`, and
(concatenated as byte arrays) `Contract.address`. Using the private key of `owner address` for signing.
using the private key of the `owner` account for signing.
From Ceres (i.e. FATE VM version 3) the
[signature](./sophia_features.md#delegation-signature) can also be generic
(allowing _all_, existing and future, names to be delegated with one
signature), i.e. containing `network id`, `owner address`, `name_hash`, and
`Contract.address`.
##### revoke ##### revoke
``` ```
AENS.revoke(owner : address, name : string, <signature : signature>) : unit AENSv2.revoke(owner : address, name : string, <signature : signature>) : unit
``` ```
Revokes the name to extend the ownership time. Revokes the name to extend the ownership time.
The [signature](./sophia_features.md#delegation-signature) should be over The [signature](./sophia_features.md#delegation-signature) should be a
`network id` + `owner address` + `name_hash` + `Contract.address` serialized structure containing `network id`, `owner address`, and
(concatenated as byte arrays) `Contract.address`. Using the private key of `owner address` for signing.
using the private key of the `owner` account for signing.
From Ceres (i.e. FATE VM version 3) the
[signature](./sophia_features.md#delegation-signature) can also be generic
(allowing _all_, existing and future, names to be delegated with one
signature), i.e. containing `network id`, `owner address`, `name_hash`, and
`Contract.address`.
##### update ##### update
``` ```
AENS.update(owner : address, name : string, expiry : option(Chain.ttl), client_ttl : option(int), AENSv2.update(owner : address, name : string, expiry : option(Chain.ttl), client_ttl : option(int),
new_ptrs : map(string, AENS.pointee), <signature : signature>) : unit new_ptrs : option(map(string, AENSv2.pointee)), <signature : signature>) : unit
``` ```
Updates the name. If the optional parameters are set to `None` that parameter Updates the name. If the optional parameters are set to `None` that parameter
will not be updated, for example if `None` is passed as `expiry` the expiry will not be updated, for example if `None` is passed as `expiry` the expiry
block of the name is not changed. block of the name is not changed.
Note: Changed to consume `AENSv2.pointee` in v8.0 (Ceres protocol upgrade).
The [signature](./sophia_features.md#delegation-signature) should be a
serialized structure containing `network id`, `owner address`, and
`Contract.address`. Using the private key of `owner address` for signing.
From Ceres (i.e. FATE VM version 3) the
[signature](./sophia_features.md#delegation-signature) can also be generic
(allowing _all_, existing and future, names to be delegated with one
signature), i.e. containing `network id`, `owner address`, `name_hash`, and
`Contract.address`.
### Auth ### Auth
@ -236,7 +303,10 @@ namespace Chain =
Auth.tx_hash : option(hash) Auth.tx_hash : option(hash)
``` ```
Gets the transaction hash during authentication. Gets the transaction hash during authentication. Note: `Auth.tx_hash`
computation differs between protocol versions (changed in Ceres!), see
[aeserialisation](https://github.com/aeternity/protocol/blob/master/serializations.md)
specification for details.
### Bits ### Bits
@ -315,7 +385,7 @@ Each bit is true if and only if it was 1 in `a` and 0 in `b`
### Bytes ### Bytes
#### to_int #### to\_int
``` ```
Bytes.to_int(b : bytes(n)) : int Bytes.to_int(b : bytes(n)) : int
``` ```
@ -323,7 +393,7 @@ Bytes.to_int(b : bytes(n)) : int
Interprets the byte array as a big endian integer Interprets the byte array as a big endian integer
#### to_str #### to\_str
``` ```
Bytes.to_str(b : bytes(n)) : string Bytes.to_str(b : bytes(n)) : string
``` ```
@ -336,7 +406,8 @@ Returns the hexadecimal representation of the byte array
Bytes.concat : (a : bytes(m), b : bytes(n)) => bytes(m + n) Bytes.concat : (a : bytes(m), b : bytes(n)) => bytes(m + n)
``` ```
Concatenates two byte arrays Concatenates two byte arrays. If `m` and `n` are known at compile time, the
result can be used as a fixed size byte array, otherwise it has type `bytes()`.
#### split #### split
@ -346,6 +417,38 @@ Bytes.split(a : bytes(m + n)) : bytes(m) * bytes(n)
Splits a byte array at given index Splits a byte array at given index
#### split\_any
```
Bytes.split_any(a : bytes(), at : int) : option(bytes() * bytes(n))
```
Splits an arbitrary size byte array at index `at`. If `at` is positive split
from the beginning of the array, if `at` is negative, split `abs(at)` from the
_end_ of the array. If the array is shorter than `abs(at)` then `None` is
returned.
#### to\_fixed\_size
```
Bytes.to_fixed_size(a : bytes()) : option(bytes(n))
```
Converts an arbitrary size byte array to a fix size byte array. If `a` is
not `n` bytes, `None` is returned.
#### to\_any\_size
```
Bytes.to_any_size(a : bytes(n)) : bytes()
```
Converts a fixed size byte array to an arbitrary size byte array. This is a
no-op at run-time, and only used during type checking.
#### size
```
Bytes.size(a : bytes()) : int
```
Computes the lenght/size of a byte array.
### Call ### Call
@ -381,6 +484,12 @@ Call.gas_price : int
The gas price of the current call. The gas price of the current call.
#### mulmod
```
Int.mulmod : (a : int, b : int, q : int) : int
```
Combined multiplication and modulus, returns `(a * b) mod q`.
#### fee #### fee
``` ```
@ -469,39 +578,6 @@ Chain.block_height : int"
The height of the current block (i.e. the block in which the current call will be included). The height of the current block (i.e. the block in which the current call will be included).
##### coinbase
```
Chain.coinbase : address
```
The address of the account that mined the current block.
##### timestamp
```
Chain.timestamp : int
```
The timestamp of the current block (unix time, milliseconds).
##### difficulty
```
Chain.difficulty : int
```
The difficulty of the current block.
##### gas
```
Chain.gas_limit : int
```
The gas limit of the current block.
##### bytecode_hash ##### bytecode_hash
``` ```
Chain.bytecode_hash : 'c => option(hash) Chain.bytecode_hash : 'c => option(hash)
@ -538,7 +614,6 @@ charging the calling contract. Note that this won't be visible in `Call.value`
in the `init` call of the new contract. It will be included in in the `init` call of the new contract. It will be included in
`Contract.balance`, however. `Contract.balance`, however.
The type `'c` must be instantiated with a contract. The type `'c` must be instantiated with a contract.
@ -565,6 +640,7 @@ main contract Market =
The typechecker must be certain about the created contract's type, so it is The typechecker must be certain about the created contract's type, so it is
worth writing it explicitly as shown in the example. worth writing it explicitly as shown in the example.
##### clone ##### clone
``` ```
Chain.clone : ( ref : 'c, gas : int, value : int, protected : bool, ... Chain.clone : ( ref : 'c, gas : int, value : int, protected : bool, ...
@ -603,8 +679,8 @@ Example usage:
``` ```
payable contract interface Auction = payable contract interface Auction =
entrypoint init : (int, string) => void entrypoint init : (int, string) => void
stateful payable entrypoint buy : (int) => () stateful payable entrypoint buy : (int) => unit
stateful entrypoint sell : (int) => () stateful entrypoint sell : (int) => unit
main contract Market = main contract Market =
type state = list(Auction) type state = list(Auction)
@ -623,11 +699,71 @@ implementation of the `init` function does not actually return `state`, but
calls `put` instead. Moreover, FATE prevents even handcrafted calls to `init`. calls `put` instead. Moreover, FATE prevents even handcrafted calls to `init`.
##### coinbase
```
Chain.coinbase : address
```
The address of the account that mined the current block.
##### difficulty
```
Chain.difficulty : int
```
The difficulty of the current block.
##### event ##### event
``` ```
Chain.event(e : event) : unit Chain.event(e : event) : unit
``` ```
Emits the event. To use this function one needs to define the `event` type as a `datatype` in the contract.
Emits the event. To use this function one needs to define the `event` type as a
`datatype` in the contract.
##### gas\_limit
```
Chain.gas_limit : int
```
The gas limit of the current block.
##### network\_id
```
Chain.network\_id : string
```
The network id of the chain.
#### poseidon
```
Crypto.poseidon(x1 : int, x2 : int) : int
```
Hash two integers (in the scalar field of BLS12-381) to another integer (in the scalar
field of BLS12-281). This is a ZK/SNARK-friendly hash function.
##### spend
```
Chain.spend(to : address, amount : int) : unit
```
Spend `amount` tokens to `to`. Will fail (and abort the contract) if contract
doesn't have `amount` tokens to transfer, or, if `to` is not `payable`.
##### timestamp
```
Chain.timestamp : int
```
The timestamp of the current block (unix time, milliseconds).
### Char ### Char
@ -705,11 +841,14 @@ Hash any object to blake2b
#### verify_sig #### verify_sig
``` ```
Crypto.verify_sig(msg : hash, pubkey : address, sig : signature) : bool Crypto.verify_sig(msg : bytes(), pubkey : address, sig : signature) : bool
``` ```
Checks if the signature of `msg` was made using private key corresponding to Checks if the signature of `msg` was made using private key corresponding to
the `pubkey` the `pubkey`.
Note: before v8 of the compiler, `msg` had type `hash` (i.e. `bytes(32)`).
#### ecverify_secp256k1 #### ecverify_secp256k1
``` ```
@ -742,12 +881,21 @@ Verifies a standard 64-byte ECDSA signature (`R || S`).
### Int ### Int
#### to_str #### to\_str
``` ```
Int.to_str : int => string Int.to_str(n : int) : string
``` ```
Casts integer to string using decimal representation Casts the integer to a string (in decimal representation).
#### to\_bytes
```
Int.to_bytes(n : int, size : int) : bytes()
```
Casts the integer to a byte array with `size` bytes (big endian, truncating if
necessary not preserving signedness). I.e. if you try to squeeze `-129` into a
single byte that will be indistinguishable from `127`.
### Map ### Map
@ -806,11 +954,11 @@ Oracle.register(<signature : bytes(64)>, acct : address, qfee : int, ttl : Chain
Registers new oracle answering questions of type `'a` with answers of type `'b`. Registers new oracle answering questions of type `'a` with answers of type `'b`.
* The `acct` is the address of the oracle to register (can be the same as the contract). * The `acct` is the address of the oracle to register (can be the same as the contract).
* `signature` is a signature proving that the contract is allowed to register the account - * The [signature](./sophia_features.md#delegation-signature) should be a
the `network id` + `account address` + `contract address` (concatenated as byte arrays) is serialized structure containing `network id`, `account address`, and
[signed](./sophia_features.md#delegation-signature) with the `contract address`. Using the private key of `account address` for signing.
private key of the account, proving you have the private key of the oracle to be. If the Proving you have the private key of the oracle to be. If the address is the same
address is the same as the contract `sign` is ignored and can be left out entirely. as the contract `sign` is ignored and can be left out entirely.
* The `qfee` is the minimum query fee to be paid by a user when asking a question of the oracle. * The `qfee` is the minimum query fee to be paid by a user when asking a question of the oracle.
* The `ttl` is the Time To Live for the oracle in key blocks, either relative to the current * The `ttl` is the Time To Live for the oracle in key blocks, either relative to the current
key block height (`RelativeTTL(delta)`) or a fixed key block height (`FixedTTL(height)`). key block height (`RelativeTTL(delta)`) or a fixed key block height (`FixedTTL(height)`).
@ -824,7 +972,7 @@ Examples:
``` ```
#### get_question #### get\_question
``` ```
Oracle.get_question(o : oracle('a, 'b), q : oracle_query('a, 'b)) : 'a Oracle.get_question(o : oracle('a, 'b), q : oracle_query('a, 'b)) : 'a
``` ```
@ -837,12 +985,11 @@ Checks what was the question of query `q` on oracle `o`
Oracle.respond(<signature : bytes(64)>, o : oracle('a, 'b), q : oracle_query('a, 'b), 'b) : unit Oracle.respond(<signature : bytes(64)>, o : oracle('a, 'b), q : oracle_query('a, 'b), 'b) : unit
``` ```
Responds to the question `q` on `o`. Responds to the question `q` on `o`. Unless the contract address is the same
Unless the contract address is the same as the oracle address the `signature` as the oracle address the `signature` (which is an optional, named argument)
(which is an optional, named argument)
needs to be provided. Proving that we have the private key of the oracle by needs to be provided. Proving that we have the private key of the oracle by
[signing](./sophia_features.md#delegation-signature) [signing](./sophia_features.md#delegation-signature) should be a serialized
the `network id` + `oracle query id` + `contract address` structure containing `network id`, `oracle query id`, and `contract address`.
#### extend #### extend
@ -855,7 +1002,7 @@ Extends TTL of an oracle.
* `o` is the oracle being extended * `o` is the oracle being extended
* `ttl` must be `RelativeTTL`. The time to live of `o` will be extended by this value. * `ttl` must be `RelativeTTL`. The time to live of `o` will be extended by this value.
#### query_fee #### query\_fee
``` ```
Oracle.query_fee(o : oracle('a, 'b)) : int Oracle.query_fee(o : oracle('a, 'b)) : int
``` ```
@ -876,7 +1023,7 @@ Asks the oracle a question.
The call fails if the oracle could expire before an answer. The call fails if the oracle could expire before an answer.
#### get_answer #### get\_answer
``` ```
Oracle.get_answer(o : oracle('a, 'b), q : oracle_query('a, 'b)) : option('b) Oracle.get_answer(o : oracle('a, 'b), q : oracle_query('a, 'b)) : option('b)
``` ```
@ -914,88 +1061,21 @@ It returns `true` iff the oracle query exist and has the expected type.
These need to be explicitly included (with `.aes` suffix) These need to be explicitly included (with `.aes` suffix)
### Bitwise ### AENSCompat
Bitwise operations on arbitrary precision integers. #### pointee\_to\_V2
#### bsr
``` ```
Bitwise.bsr(n : int, x : int) : int AENSCompat.pointee_to_V2(p : AENS.pointee) : AENSv2.pointee
``` ```
Logical bit shift `x` right `n` positions. Translate old pointee format to new, this is always possible.
#### pointee\_from\_V2
#### bsl
``` ```
Bitwise.bsl(n : int, x : int) : int AENSCompat.pointee_from_V2(p2 : AENSv2.pointee) : option(AENS.pointee)
``` ```
Logical bit shift `x` left `n` positions. Translate new pointee format to old, `DataPt` can't be translated, so `None` is returned in this case.
#### bsli
```
Bitwise.bsli(n : int, x : int, lim : int) : int
```
Logical bit shift `x` left `n` positions, limit to `lim` bits.
#### band
```
Bitwise.band(x : int, y : int) : int
```
Bitwise `and` of `x` and `y`.
#### bor
```
Bitwise.bor(x : int, y : int) : int
```
Bitwise `or` of `x` and `y`.
#### bxor
```
Bitwise.bxor(x : int, y : int) : int
```
Bitwise `xor` of `x` and `y`.
#### bnot
```
Bitwise.bnot(x : int) : int
```
Bitwise `not` of `x`. Defined and implemented as `bnot(x) = bxor(x, -1)`.
#### uband
```
Bitwise.uband(x : int, y : int) : int
```
Bitwise `and` of _non-negative_ numbers `x` and `y`.
#### ubor
```
Bitwise.ubor(x : int, y : int) : int
```
Bitwise `or` of _non-negative_ `x` and `y`.
#### ubxor
```
Bitwise.ubxor(x : int, y : int) : int
```
Bitwise `xor` of _non-negative_ `x` and `y`.
### BLS12\_381 ### BLS12\_381
@ -2391,6 +2471,15 @@ to_int(s : string) : option(int)
Converts a decimal ("123", "-253") or a hexadecimal ("0xa2f", "-0xBBB") string into Converts a decimal ("123", "-253") or a hexadecimal ("0xa2f", "-0xBBB") string into
an integer. If the string doesn't contain a valid number `None` is returned. an integer. If the string doesn't contain a valid number `None` is returned.
#### to\_bytes
```
to_bytes(s : string) : bytes()
```
Converts string into byte array. String is UTF-8 encoded. I.e.
`String.length(s)` is not guaranteed to be equal to
`Bytes.size(String.to_bytes(s))`.
#### sha3 #### sha3
``` ```
sha3(s : string) : hash sha3(s : string) : hash

View File

@ -30,6 +30,7 @@ interface main using as for hiding
- `ContractAddress` base58-encoded 32 byte contract address with `ct_` prefix - `ContractAddress` base58-encoded 32 byte contract address with `ct_` prefix
- `OracleAddress` base58-encoded 32 byte oracle address with `ok_` prefix - `OracleAddress` base58-encoded 32 byte oracle address with `ok_` prefix
- `OracleQueryId` base58-encoded 32 byte oracle query id with `oq_` prefix - `OracleQueryId` base58-encoded 32 byte oracle query id with `oq_` prefix
- `Signature` base58-encoded 64 byte cryptographic signature with `sg_` prefix
Valid string escape codes are Valid string escape codes are
@ -239,6 +240,7 @@ Expr ::= '(' LamArgs ')' '=>' Block(Stmt) // Anonymous function (x) => x +
| Int | Bytes | String | Char // Literals 123, 0xff, #00abc123, "foo", '%' | Int | Bytes | String | Char // Literals 123, 0xff, #00abc123, "foo", '%'
| AccountAddress | ContractAddress // Chain identifiers | AccountAddress | ContractAddress // Chain identifiers
| OracleAddress | OracleQueryId // Chain identifiers | OracleAddress | OracleQueryId // Chain identifiers
| Signature // Signature
| '???' // Hole expression 1 + ??? | '???' // Hole expression 1 + ???
Generator ::= Pattern '<-' Expr // Generator Generator ::= Pattern '<-' Expr // Generator
@ -256,8 +258,8 @@ Path ::= Id // Record field
BinOp ::= '||' | '&&' | '<' | '>' | '=<' | '>=' | '==' | '!=' BinOp ::= '||' | '&&' | '<' | '>' | '=<' | '>=' | '==' | '!='
| '::' | '++' | '+' | '-' | '*' | '/' | 'mod' | '^' | '::' | '++' | '+' | '-' | '*' | '/' | 'mod' | '^'
| '|>' | 'band' | 'bor' | 'bxor' | '<<' | '>>' | '|>'
UnOp ::= '-' | '!' UnOp ::= '-' | '!' | 'bnot'
``` ```
## Operators types ## Operators types
@ -265,10 +267,11 @@ UnOp ::= '-' | '!'
| Operators | Type | Operators | Type
| --- | --- | --- | ---
| `-` `+` `*` `/` `mod` `^` | arithmetic operators | `-` `+` `*` `/` `mod` `^` | arithmetic operators
| `!` `&&` `||` | logical operators | `!` `&&` `\|\|` | logical operators
| `band` `bor` `bxor` `bnot` `<<` `>>` | bitwise operators
| `==` `!=` `<` `>` `=<` `>=` | comparison operators | `==` `!=` `<` `>` `=<` `>=` | comparison operators
| `::` `++` | list operators | `::` `++` | list operators
| `|>` | functional operators | `\|>` | functional operators
## Operator precedence ## Operator precedence
@ -276,13 +279,17 @@ In order of highest to lowest precedence.
| Operators | Associativity | Operators | Associativity
| --- | --- | --- | ---
| `!` | right | `!` `bnot`| right
| `^` | left | `^` | left
| `*` `/` `mod` | left | `*` `/` `mod` | left
| `-` (unary) | right | `-` (unary) | right
| `+` `-` | left | `+` `-` | left
| `<<` `>>` | left
| `::` `++` | right | `::` `++` | right
| `<` `>` `=<` `>=` `==` `!=` | none | `<` `>` `=<` `>=` `==` `!=` | none
| `band` | left
| `bxor` | left
| `bor` | left
| `&&` | right | `&&` | right
| `||` | right | `\|\|` | right
| `|>` | left | `\|>` | left

View File

@ -0,0 +1,17 @@
namespace AENSCompat =
// Translate old format to new format - always possible
function pointee_to_V2(p : AENS.pointee) : AENSv2.pointee =
switch(p)
AENS.AccountPt(a) => AENSv2.AccountPt(a)
AENS.OraclePt(a) => AENSv2.OraclePt(a)
AENS.ContractPt(a) => AENSv2.ContractPt(a)
AENS.ChannelPt(a) => AENSv2.ChannelPt(a)
// Translate new format to old format - option type!
function pointee_from_V2(p2 : AENSv2.pointee) : option(AENS.pointee) =
switch(p2)
AENSv2.AccountPt(a) => Some(AENS.AccountPt(a))
AENSv2.OraclePt(a) => Some(AENS.OraclePt(a))
AENSv2.ContractPt(a) => Some(AENS.ContractPt(a))
AENSv2.ChannelPt(a) => Some(AENS.ChannelPt(a))
AENSv2.DataPt(_) => None

View File

@ -1,126 +0,0 @@
@compiler >= 4.3
namespace Bitwise =
// bit shift 'x' right 'n' postions
function bsr(n : int, x : int) : int =
let step = 2^n
let res = x / step
if (x >= 0 || x mod step == 0)
res
else
res - 1
// bit shift 'x' left 'n' positions
function bsl(n : int, x : int) : int =
x * 2^n
// bit shift 'x' left 'n' positions, limit at 'lim' bits
function bsli(n : int, x : int, lim : int) : int =
(x * 2^n) mod (2^lim)
// bitwise 'and' for arbitrary precision integers
function band(a : int, b : int) : int =
if (a >= 0 && b >= 0)
uband_(a, b)
elif (b >= 0)
ubnand_(b, -1 - a)
elif (a >= 0)
ubnand_(a, -1 - b)
else
-1 - ubor_(-1 - a, -1 - b)
// bitwise 'or' for arbitrary precision integers
function
bor : (int, int) => int
bor(0, b) = b
bor(a, 0) = a
bor(a : int, b : int) : int =
if (a >= 0 && b >= 0)
ubor_(a, b)
elif (b >= 0)
-1 - ubnand_(-1 - a, b)
elif (a >= 0)
-1 - ubnand_(-1 - b, a)
else
-1 - uband_(-1 - a, -1 - b)
// bitwise 'xor' for arbitrary precision integers
function
bxor : (int, int) => int
bxor(0, b) = b
bxor(a, 0) = a
bxor(a, b) =
if (a >= 0 && b >= 0)
ubxor_(a, b)
elif (b >= 0)
-1 - ubxor_(-1 - a, b)
elif (a >= 0)
-1 - ubxor_(a, -1 - b)
else
ubxor_(-1 - a, -1 - b)
// bitwise 'not' for arbitrary precision integers
function bnot(a : int) = bxor(a, -1)
// Bitwise 'and' for non-negative integers
function uband(a : int, b : int) : int =
require(a >= 0 && b >= 0, "uband is only defined for non-negative integers")
switch((a, b))
(0, _) => 0
(_, 0) => 0
_ => uband__(a, b, 1, 0)
private function uband_(a, b) = uband__(a, b, 1, 0)
private function
uband__(0, b, val, acc) = acc
uband__(a, 0, val, acc) = acc
uband__(a, b, val, acc) =
switch (a mod 2 + b mod 2)
2 => uband__(a / 2, b / 2, val * 2, acc + val)
_ => uband__(a / 2, b / 2, val * 2, acc)
// Bitwise 'or' for non-negative integers
function ubor(a, b) =
require(a >= 0 && b >= 0, "ubor is only defined for non-negative integers")
switch((a, b))
(0, _) => b
(_, 0) => a
_ => ubor__(a, b, 1, 0)
private function ubor_(a, b) = ubor__(a, b, 1, 0)
private function
ubor__(0, 0, val, acc) = acc
ubor__(a, b, val, acc) =
switch (a mod 2 + b mod 2)
0 => ubor__(a / 2, b / 2, val * 2, acc)
_ => ubor__(a / 2, b / 2, val * 2, acc + val)
//Bitwise 'xor' for non-negative integers
function
ubxor : (int, int) => int
ubxor(0, b) = b
ubxor(a, 0) = a
ubxor(a, b) =
require(a >= 0 && b >= 0, "ubxor is only defined for non-negative integers")
ubxor__(a, b, 1, 0)
private function ubxor_(a, b) = ubxor__(a, b, 1, 0)
private function
ubxor__(0, 0, val, acc) = acc
ubxor__(a, b, val, acc) =
switch(a mod 2 + b mod 2)
1 => ubxor__(a / 2, b / 2, val * 2, acc + val)
_ => ubxor__(a / 2, b / 2, val * 2, acc)
private function ubnand_(a, b) = ubnand__(a, b, 1, 0)
private function
ubnand__(0, b, val, acc) = acc
ubnand__(a, b, val, acc) =
switch((a mod 2, b mod 2))
(1, 0) => ubnand__(a / 2, b / 2, val * 2, acc + val)
_ => ubnand__(a / 2, b / 2, val * 2, acc)

View File

@ -282,9 +282,9 @@ namespace List =
private function private function
asc : (('a, 'a) => bool, 'a, list('a), list('a)) => list(list('a)) asc : (('a, 'a) => bool, 'a, list('a), list('a)) => list(list('a))
asc(lt, x, acc, h::t) = asc(lt, x, acc, h::t) =
if(lt(h, x)) List.reverse(x::acc) :: monotonic_subs(lt, h::t) if(lt(h, x)) reverse(x::acc) :: monotonic_subs(lt, h::t)
else asc(lt, h, x::acc, t) else asc(lt, h, x::acc, t)
asc(_, x, acc, []) = [List.reverse(x::acc)] asc(_, x, acc, []) = [reverse(x::acc)]
/** Merges list of sorted lists /** Merges list of sorted lists
*/ */

View File

@ -1,5 +1,8 @@
include "List.aes" include "List.aes"
namespace String = namespace String =
// Gives a bytes() representation of the string
function to_bytes(s : string) : bytes() = StringInternal.to_bytes(s)
// Computes the SHA3/Keccak hash of the string // Computes the SHA3/Keccak hash of the string
function sha3(s : string) : hash = StringInternal.sha3(s) function sha3(s : string) : hash = StringInternal.sha3(s)
// Computes the SHA256 hash of the string. // Computes the SHA256 hash of the string.

22
rebar.config Normal file
View File

@ -0,0 +1,22 @@
%% -*- mode: erlang; indent-tabs-mode: nil -*-
{erl_opts, [debug_info]}.
{deps, [ {aebytecode, {git, "https://github.com/aeternity/aebytecode.git", {tag, "v3.4.1"}}}
, {eblake2, "1.0.0"}
, {jsx, {git, "https://github.com/talentdeficit/jsx.git", {tag, "2.8.0"}}}
]}.
{dialyzer, [
{warnings, [unknown]},
{plt_apps, all_deps},
{base_plt_apps, [erts, kernel, stdlib, crypto, mnesia]}
]}.
{relx, [{release, {aesophia, "8.0.1"},
[aesophia, aebytecode]},
{dev_mode, true},
{include_erts, false},
{extended_start_script, true}]}.

31
rebar.lock Normal file
View File

@ -0,0 +1,31 @@
{"1.2.0",
[{<<"aebytecode">>,
{git,"https://github.com/aeternity/aebytecode.git",
{ref,"6bd6f82c70d800950ea1a2c70c364a4181ff5291"}},
0},
{<<"aeserialization">>,
{git,"https://github.com/aeternity/aeserialization.git",
{ref,"b26e6d105424748ba1c27917267b7cff07f37802"}},
1},
{<<"base58">>,
{git,"https://github.com/aeternity/erl-base58.git",
{ref,"60a335668a60328a29f9731b67c4a0e9e3d50ab6"}},
2},
{<<"eblake2">>,{pkg,<<"eblake2">>,<<"1.0.0">>},0},
{<<"enacl">>,
{git,"https://github.com/aeternity/enacl.git",
{ref,"4eb7ec70084ba7c87b1af8797c4c4e90c84f95a2"}},
2},
{<<"getopt">>,{pkg,<<"getopt">>,<<"1.0.1">>},1},
{<<"jsx">>,
{git,"https://github.com/talentdeficit/jsx.git",
{ref,"3074d4865b3385a050badf7828ad31490d860df5"}},
0}]}.
[
{pkg_hash,[
{<<"eblake2">>, <<"EC8AD20E438AAB3F2E8D5D118C366A0754219195F8A0F536587440F8F9BCF2EF">>},
{<<"getopt">>, <<"C73A9FA687B217F2FF79F68A3B637711BB1936E712B521D8CE466B29CBF7808A">>}]},
{pkg_hash_ext,[
{<<"eblake2">>, <<"3C4D300A91845B25D501929A26AC2E6F7157480846FAB2347A4C11AE52E08A99">>},
{<<"getopt">>, <<"53E1AB83B9CEB65C9672D3E7A35B8092E9BDC9B3EE80721471A161C10C59959C">>}]}
].

View File

@ -199,7 +199,8 @@ encode_expr({bytes, _, B}) ->
<<N:Digits/unit:8>> = B, <<N:Digits/unit:8>> = B,
list_to_binary(lists:flatten(io_lib:format("#~*.16.0b", [Digits*2, N]))); list_to_binary(lists:flatten(io_lib:format("#~*.16.0b", [Digits*2, N])));
encode_expr({Lit, _, L}) when Lit == oracle_pubkey; Lit == oracle_query_id; encode_expr({Lit, _, L}) when Lit == oracle_pubkey; Lit == oracle_query_id;
Lit == contract_pubkey; Lit == account_pubkey -> Lit == contract_pubkey; Lit == account_pubkey;
Lit == signature ->
aeser_api_encoder:encode(Lit, L); aeser_api_encoder:encode(Lit, L);
encode_expr({app, _, {'-', _}, [{int, _, N}]}) -> encode_expr({app, _, {'-', _}, [{int, _, N}]}) ->
encode_expr({int, [], -N}); encode_expr({int, [], -N});
@ -308,6 +309,8 @@ decode_type(#{list := [Et]}) ->
decode_type(#{map := Ets}) -> decode_type(#{map := Ets}) ->
Ts = decode_types(Ets), Ts = decode_types(Ets),
["map",$(,lists:join(",", Ts),$)]; ["map",$(,lists:join(",", Ts),$)];
decode_type(#{bytes := any}) ->
["bytes()"];
decode_type(#{bytes := Len}) -> decode_type(#{bytes := Len}) ->
["bytes(", integer_to_list(Len), ")"]; ["bytes(", integer_to_list(Len), ")"];
decode_type(#{variant := Ets}) -> decode_type(#{variant := Ets}) ->

View File

@ -90,8 +90,9 @@
-type field_constraint() :: #field_constraint{} | #record_create_constraint{} | #is_contract_constraint{}. -type field_constraint() :: #field_constraint{} | #record_create_constraint{} | #is_contract_constraint{}.
-type byte_constraint() :: {is_bytes, utype()} -type byte_constraint() :: {is_bytes, term(), utype()}
| {add_bytes, aeso_syntax:ann(), concat | split, utype(), utype(), utype()}. | {is_fixed_bytes, term(), utype()}
| {add_bytes, aeso_syntax:ann(), concat | split | split_any, utype(), utype(), utype()}.
-type aens_resolve_constraint() :: {aens_resolve_type, utype()}. -type aens_resolve_constraint() :: {aens_resolve_type, utype()}.
-type oracle_type_constraint() :: {oracle_type, aeso_syntax:ann(), utype()}. -type oracle_type_constraint() :: {oracle_type, aeso_syntax:ann(), utype()}.
@ -155,7 +156,6 @@
, in_pattern = false :: boolean() , in_pattern = false :: boolean()
, in_guard = false :: boolean() , in_guard = false :: boolean()
, stateful = false :: boolean() , stateful = false :: boolean()
, unify_throws = true :: boolean()
, current_const = none :: none | aeso_syntax:id() , current_const = none :: none | aeso_syntax:id()
, current_function = none :: none | aeso_syntax:id() , current_function = none :: none | aeso_syntax:id()
, what = top :: top | namespace | contract | contract_interface , what = top :: top | namespace | contract | contract_interface
@ -352,11 +352,11 @@ bind_contract(Typing, {Contract, Ann, Id, _Impls, Contents}, Env)
Sys = [{origin, system}], Sys = [{origin, system}],
TypeOrFresh = fun({typed, _, _, Type}) -> Type; (_) -> fresh_uvar(Sys) end, TypeOrFresh = fun({typed, _, _, Type}) -> Type; (_) -> fresh_uvar(Sys) end,
Fields = Fields =
[ {field_t, AnnF, Entrypoint, contract_call_type(Type)} [ {field_t, AnnF, Entrypoint, contract_call_type(aeso_syntax:set_ann(Sys, Type))}
|| {fun_decl, AnnF, Entrypoint, Type = {fun_t, _, _, _, _}} <- Contents ] ++ || {fun_decl, AnnF, Entrypoint, Type = {fun_t, _, _, _, _}} <- Contents ] ++
[ {field_t, AnnF, Entrypoint, [ {field_t, AnnF, Entrypoint,
contract_call_type( contract_call_type(
{fun_t, AnnF, [], [TypeOrFresh(Arg) || Arg <- Args], TypeOrFresh(Ret)}) {fun_t, Sys, [], [TypeOrFresh(Arg) || Arg <- Args], TypeOrFresh(Ret)})
} }
|| {letfun, AnnF, Entrypoint = {id, _, Name}, Args, _Type, [{guarded, _, [], Ret}]} <- Contents, || {letfun, AnnF, Entrypoint = {id, _, Name}, Args, _Type, [{guarded, _, [], Ret}]} <- Contents,
Name =/= "init" Name =/= "init"
@ -595,6 +595,8 @@ global_env() ->
TTL = {qid, Ann, ["Chain", "ttl"]}, TTL = {qid, Ann, ["Chain", "ttl"]},
Pointee = {qid, Ann, ["AENS", "pointee"]}, Pointee = {qid, Ann, ["AENS", "pointee"]},
AENSName = {qid, Ann, ["AENS", "name"]}, AENSName = {qid, Ann, ["AENS", "name"]},
PointeeV2 = {qid, Ann, ["AENSv2", "pointee"]},
AENSNameV2 = {qid, Ann, ["AENSv2", "name"]},
Fr = {qid, Ann, ["MCL_BLS12_381", "fr"]}, Fr = {qid, Ann, ["MCL_BLS12_381", "fr"]},
Fp = {qid, Ann, ["MCL_BLS12_381", "fp"]}, Fp = {qid, Ann, ["MCL_BLS12_381", "fp"]},
Fp2 = {tuple_t, Ann, [Fp, Fp]}, Fp2 = {tuple_t, Ann, [Fp, Fp]},
@ -652,6 +654,7 @@ global_env() ->
{"block_height", Int}, {"block_height", Int},
{"difficulty", Int}, {"difficulty", Int},
{"gas_limit", Int}, {"gas_limit", Int},
{"network_id", String},
{"bytecode_hash",FunC1(bytecode_hash, A, Option(Hash))}, {"bytecode_hash",FunC1(bytecode_hash, A, Option(Hash))},
{"create", Stateful( {"create", Stateful(
FunN([ {named_arg_t, Ann, {id, Ann, "value"}, Int, {typed, Ann, {int, Ann, 0}, Int}} FunN([ {named_arg_t, Ann, {id, Ann, "value"}, Int, {typed, Ann, {int, Ann, 0}, Int}}
@ -730,14 +733,7 @@ global_env() ->
AENSScope = #scope AENSScope = #scope
{ funs = MkDefs( { funs = MkDefs(
[{"resolve", Fun([String, String], option_t(Ann, A))}, [%% AENS pointee constructors
{"preclaim", SignFun([Address, Hash], Unit)},
{"claim", SignFun([Address, String, Int, Int], Unit)},
{"transfer", SignFun([Address, Address, String], Unit)},
{"revoke", SignFun([Address, String], Unit)},
{"update", SignFun([Address, String, Option(TTL), Option(Int), Option(Map(String, Pointee))], Unit)},
{"lookup", Fun([String], option_t(Ann, AENSName))},
%% AENS pointee constructors
{"AccountPt", Fun1(Address, Pointee)}, {"AccountPt", Fun1(Address, Pointee)},
{"OraclePt", Fun1(Address, Pointee)}, {"OraclePt", Fun1(Address, Pointee)},
{"ContractPt", Fun1(Address, Pointee)}, {"ContractPt", Fun1(Address, Pointee)},
@ -747,6 +743,26 @@ global_env() ->
]) ])
, types = MkDefs([{"pointee", 0}, {"name", 0}]) }, , types = MkDefs([{"pointee", 0}, {"name", 0}]) },
AENSv2Scope = #scope
{ funs = MkDefs(
[{"resolve", Fun([String, String], option_t(Ann, A))},
{"preclaim", SignFun([Address, Hash], Unit)},
{"claim", SignFun([Address, String, Int, Int], Unit)},
{"transfer", SignFun([Address, Address, String], Unit)},
{"revoke", SignFun([Address, String], Unit)},
{"update", SignFun([Address, String, Option(TTL), Option(Int), Option(Map(String, PointeeV2))], Unit)},
{"lookup", Fun([String], option_t(Ann, AENSNameV2))},
%% AENS pointee constructors v2
{"AccountPt", Fun1(Address, PointeeV2)},
{"OraclePt", Fun1(Address, PointeeV2)},
{"ContractPt", Fun1(Address, PointeeV2)},
{"ChannelPt", Fun1(Address, PointeeV2)},
{"DataPt", Fun1(Bytes(any), PointeeV2)},
%% Name object constructor v2
{"Name", Fun([Address, TTL, Map(String, PointeeV2)], AENSNameV2)}
])
, types = MkDefs([{"pointee", 0}, {"name", 0}]) },
MapScope = #scope MapScope = #scope
{ funs = MkDefs( { funs = MkDefs(
[{"from_list", Fun1(List(Pair(K, V)), Map(K, V))}, [{"from_list", Fun1(List(Pair(K, V)), Map(K, V))},
@ -760,13 +776,14 @@ global_env() ->
%% Crypto/Curve operations %% Crypto/Curve operations
CryptoScope = #scope CryptoScope = #scope
{ funs = MkDefs( { funs = MkDefs(
[{"verify_sig", Fun([Hash, Address, SignId], Bool)}, [{"verify_sig", Fun([Bytes('_'), Address, SignId], Bool)},
{"verify_sig_secp256k1", Fun([Hash, Bytes(64), SignId], Bool)}, {"verify_sig_secp256k1", Fun([Hash, Bytes(64), SignId], Bool)},
{"ecverify_secp256k1", Fun([Hash, Bytes(20), Bytes(65)], Bool)}, {"ecverify_secp256k1", Fun([Hash, Bytes(20), Bytes(65)], Bool)},
{"ecrecover_secp256k1", Fun([Hash, Bytes(65)], Option(Bytes(20)))}, {"ecrecover_secp256k1", Fun([Hash, Bytes(65)], Option(Bytes(20)))},
{"sha3", Fun1(A, Hash)}, {"sha3", Fun1(A, Hash)},
{"sha256", Fun1(A, Hash)}, {"sha256", Fun1(A, Hash)},
{"blake2b", Fun1(A, Hash)}]) }, {"blake2b", Fun1(A, Hash)},
{"poseidon", Fun([Int, Int], Int)}]) },
%% Fancy BLS12-381 crypto operations %% Fancy BLS12-381 crypto operations
MCL_BLS12_381_Scope = #scope MCL_BLS12_381_Scope = #scope
@ -814,6 +831,7 @@ global_env() ->
[{"length", Fun1(String, Int)}, [{"length", Fun1(String, Int)},
{"concat", Fun([String, String], String)}, {"concat", Fun([String, String], String)},
{"to_list", Fun1(String, List(Char))}, {"to_list", Fun1(String, List(Char))},
{"to_bytes", Fun1(String, Bytes(any))},
{"from_list", Fun1(List(Char), String)}, {"from_list", Fun1(List(Char), String)},
{"to_upper", Fun1(String, String)}, {"to_upper", Fun1(String, String)},
{"to_lower", Fun1(String, String)}, {"to_lower", Fun1(String, String)},
@ -844,21 +862,28 @@ global_env() ->
%% Bytes %% Bytes
BytesScope = #scope BytesScope = #scope
{ funs = MkDefs( { funs = MkDefs(
[{"to_int", Fun1(Bytes(any), Int)}, [{"to_int", Fun1(Bytes('_'), Int)},
{"to_str", Fun1(Bytes(any), String)}, {"to_str", Fun1(Bytes('_'), String)},
{"concat", FunC(bytes_concat, [Bytes(any), Bytes(any)], Bytes(any))}, {"to_fixed_size", Fun1(Bytes(any), Option(Bytes(fixed)))},
{"split", FunC(bytes_split, [Bytes(any)], Pair(Bytes(any), Bytes(any)))} {"to_any_size", Fun1(Bytes(fixed), Bytes(any))},
{"size", Fun1(Bytes('_'), Int)},
{"concat", FunC(bytes_concat, [Bytes('_'), Bytes('_')], Bytes('_'))},
{"split", FunC1(bytes_split, Bytes(fixed), Pair(Bytes(fixed), Bytes(fixed)))},
{"split_any", Fun([Bytes(any), Int], Option(Pair(Bytes(any), Bytes(any))))}
]) }, ]) },
%% Conversion %% Conversion
IntScope = #scope{ funs = MkDefs([{"to_str", Fun1(Int, String)}]) }, IntScope = #scope{ funs = MkDefs([{"to_str", Fun1(Int, String)},
{"to_bytes", Fun([Int, Int], Bytes(any))},
{"mulmod", Fun([Int, Int, Int], Int)}]) },
AddressScope = #scope{ funs = MkDefs([{"to_str", Fun1(Address, String)}, AddressScope = #scope{ funs = MkDefs([{"to_str", Fun1(Address, String)},
{"to_bytes", Fun1(Address, Bytes(32))},
{"to_contract", FunC(address_to_contract, [Address], A)}, {"to_contract", FunC(address_to_contract, [Address], A)},
{"is_oracle", Fun1(Address, Bool)}, {"is_oracle", Fun1(Address, Bool)},
{"is_contract", Fun1(Address, Bool)}, {"is_contract", Fun1(Address, Bool)},
{"is_payable", Fun1(Address, Bool)}]) }, {"is_payable", Fun1(Address, Bool)}]) },
#env{ scopes = #env{ scopes =
#{ [] => TopScope #{ [] => TopScope
, ["Chain"] => ChainScope , ["Chain"] => ChainScope
@ -866,6 +891,7 @@ global_env() ->
, ["Call"] => CallScope , ["Call"] => CallScope
, ["Oracle"] => OracleScope , ["Oracle"] => OracleScope
, ["AENS"] => AENSScope , ["AENS"] => AENSScope
, ["AENSv2"] => AENSv2Scope
, ["Map"] => MapScope , ["Map"] => MapScope
, ["Auth"] => AuthScope , ["Auth"] => AuthScope
, ["Crypto"] => CryptoScope , ["Crypto"] => CryptoScope
@ -926,14 +952,14 @@ infer(Contracts, Options) ->
{Env1, Decls} = infer1(Env, Contracts1, [], Options), {Env1, Decls} = infer1(Env, Contracts1, [], Options),
when_warning(warn_unused_functions, fun() -> destroy_and_report_unused_functions() end), when_warning(warn_unused_functions, fun() -> destroy_and_report_unused_functions() end),
when_option(warn_error, fun() -> destroy_and_report_warnings_as_type_errors() end), when_option(warn_error, fun() -> destroy_and_report_warnings_as_type_errors() end),
WarningsUnsorted = lists:map(fun mk_warning/1, ets_tab2list(warnings)),
Warnings = aeso_warnings:sort_warnings(WarningsUnsorted),
{Env2, DeclsFolded, DeclsUnfolded} = {Env2, DeclsFolded, DeclsUnfolded} =
case proplists:get_value(dont_unfold, Options, false) of case proplists:get_value(dont_unfold, Options, false) of
true -> {Env1, Decls, Decls}; true -> {Env1, Decls, Decls};
false -> E = on_scopes(Env1, fun(Scope) -> unfold_record_types(Env1, Scope) end), false -> E = on_scopes(Env1, fun(Scope) -> unfold_record_types(Env1, Scope) end),
{E, Decls, unfold_record_types(E, Decls)} {E, Decls, unfold_record_types(E, Decls)}
end, end,
WarningsUnsorted = lists:map(fun mk_warning/1, ets_tab2list(warnings)),
Warnings = aeso_warnings:sort_warnings(WarningsUnsorted),
case proplists:get_value(return_env, Options, false) of case proplists:get_value(return_env, Options, false) of
false -> {DeclsFolded, DeclsUnfolded, Warnings}; false -> {DeclsFolded, DeclsUnfolded, Warnings};
true -> {Env2, DeclsFolded, DeclsUnfolded, Warnings} true -> {Env2, DeclsFolded, DeclsUnfolded, Warnings}
@ -1136,7 +1162,7 @@ infer_contract(Env0, What, Defs0, Options) ->
_ = bind_funs(lists:map(FunBind, Functions), #env{}), _ = bind_funs(lists:map(FunBind, Functions), #env{}),
FunMap = maps:from_list([ {FunName(Def), Def} || Def <- Functions ]), FunMap = maps:from_list([ {FunName(Def), Def} || Def <- Functions ]),
check_reserved_entrypoints(FunMap), check_reserved_entrypoints(FunMap),
DepGraph = maps:map(fun(_, Def) -> aeso_syntax_utils:used_ids(Def) end, FunMap), DepGraph = maps:map(fun(_, Def) -> aeso_syntax_utils:used_ids(Env3#env.namespace, Def) end, FunMap),
SCCs = aeso_utils:scc(DepGraph), SCCs = aeso_utils:scc(DepGraph),
{Env4, Defs1} = check_sccs(Env3, FunMap, SCCs, []), {Env4, Defs1} = check_sccs(Env3, FunMap, SCCs, []),
%% Remove namespaces used in the current namespace %% Remove namespaces used in the current namespace
@ -1583,7 +1609,7 @@ check_reserved_entrypoints(Funs) ->
check_fundecl(Env, {fun_decl, Ann, Id = {id, _, Name}, Type = {fun_t, _, _, _, _}}) -> check_fundecl(Env, {fun_decl, Ann, Id = {id, _, Name}, Type = {fun_t, _, _, _, _}}) ->
Type1 = {fun_t, _, Named, Args, Ret} = check_type(Env, Type), Type1 = {fun_t, _, Named, Args, Ret} = check_type(Env, Type),
TypeSig = {type_sig, Ann, none, Named, Args, Ret}, TypeSig = {type_sig, Ann, none, Named, Args, Ret},
register_implementation(Id, TypeSig), register_implementation(Env, Id, TypeSig),
{{Name, TypeSig}, {fun_decl, Ann, Id, Type1}}; {{Name, TypeSig}, {fun_decl, Ann, Id, Type1}};
check_fundecl(Env, {fun_decl, Ann, Id = {id, _, Name}, Type}) -> check_fundecl(Env, {fun_decl, Ann, Id = {id, _, Name}, Type}) ->
type_error({fundecl_must_have_funtype, Ann, Id, Type}), type_error({fundecl_must_have_funtype, Ann, Id, Type}),
@ -1591,13 +1617,16 @@ check_fundecl(Env, {fun_decl, Ann, Id = {id, _, Name}, Type}) ->
%% Register the function FunId as implemented by deleting it from the functions %% Register the function FunId as implemented by deleting it from the functions
%% to be implemented table if it is included there, or return true otherwise. %% to be implemented table if it is included there, or return true otherwise.
-spec register_implementation(FunId, FunSig) -> true | no_return() when -spec register_implementation(env(), FunId, FunSig) -> true | no_return() when
FunId :: aeso_syntax:id(), FunId :: aeso_syntax:id(),
FunSig :: typesig(). FunSig :: typesig().
register_implementation(Id, Sig) -> register_implementation(Env, Id, Sig) ->
Name = name(Id), Name = name(Id),
case ets_lookup(functions_to_implement, Name) of case ets_lookup(functions_to_implement, Name) of
[{Name, Interface, Decl = {fun_decl, _, DeclId, _}}] -> [{Name, Interface, Decl = {fun_decl, _, DeclId, FunT}}] ->
When = {implement_interface_fun, aeso_syntax:get_ann(Sig), Name, name(Interface)},
unify(Env, typesig_to_fun_t(Sig), FunT, When),
DeclStateful = aeso_syntax:get_ann(stateful, Decl, false), DeclStateful = aeso_syntax:get_ann(stateful, Decl, false),
DeclPayable = aeso_syntax:get_ann(payable, Decl, false), DeclPayable = aeso_syntax:get_ann(payable, Decl, false),
@ -1625,7 +1654,7 @@ infer_nonrec(Env, LetFun) ->
create_constraints(), create_constraints(),
NewLetFun = {{_, Sig}, _} = infer_letfun(Env, LetFun), NewLetFun = {{_, Sig}, _} = infer_letfun(Env, LetFun),
check_special_funs(Env, NewLetFun), check_special_funs(Env, NewLetFun),
register_implementation(get_letfun_id(LetFun), Sig), register_implementation(Env, get_letfun_id(LetFun), Sig),
solve_then_destroy_and_report_unsolved_constraints(Env), solve_then_destroy_and_report_unsolved_constraints(Env),
Result = {TypeSig, _} = instantiate(NewLetFun), Result = {TypeSig, _} = instantiate(NewLetFun),
print_typesig(TypeSig), print_typesig(TypeSig),
@ -1655,11 +1684,11 @@ infer_letrec(Env, Defs) ->
Inferred = Inferred =
[ begin [ begin
Res = {{Name, TypeSig}, LetFun} = infer_letfun(ExtendEnv, LF), Res = {{Name, TypeSig}, LetFun} = infer_letfun(ExtendEnv, LF),
register_implementation(get_letfun_id(LetFun), TypeSig), register_implementation(Env, get_letfun_id(LetFun), TypeSig),
Got = proplists:get_value(Name, Funs), Got = proplists:get_value(Name, Funs),
Expect = typesig_to_fun_t(TypeSig), Expect = typesig_to_fun_t(TypeSig),
unify(Env, Got, Expect, {check_typesig, Name, Got, Expect}), unify(Env, Got, Expect, {check_typesig, Name, Got, Expect}),
solve_constraints(Env), solve_all_constraints(Env),
?PRINT_TYPES("Checked ~s : ~s\n", ?PRINT_TYPES("Checked ~s : ~s\n",
[Name, pp(dereference_deep(Got))]), [Name, pp(dereference_deep(Got))]),
Res Res
@ -1732,8 +1761,27 @@ desugar_clauses(Ann, Fun, {type_sig, _, _, _, ArgTypes, RetType}, Clauses) ->
end. end.
print_typesig({Name, TypeSig}) -> print_typesig({Name, TypeSig}) ->
assert_tvars(Name, TypeSig),
?PRINT_TYPES("Inferred ~s : ~s\n", [Name, pp(TypeSig)]). ?PRINT_TYPES("Inferred ~s : ~s\n", [Name, pp(TypeSig)]).
assert_tvars(Name, TS) ->
TVars = assert_tvars_(TS, #{}),
case maps:size(TVars) > 256 of
true ->
type_error({too_many_tvars, Name, TS});
false ->
ok
end.
assert_tvars_({tvar, _, TV}, TVars) ->
TVars#{TV => ok};
assert_tvars_(T, TVars) when is_tuple(T) ->
assert_tvars_(tuple_to_list(T), TVars);
assert_tvars_(Ts, TVars) when is_list(Ts) ->
lists:foldl(fun(T, TVars1) -> assert_tvars_(T, TVars1) end, TVars, Ts);
assert_tvars_(_, TVars) ->
TVars.
arg_type(ArgAnn, {id, Ann, "_"}) -> arg_type(ArgAnn, {id, Ann, "_"}) ->
case aeso_syntax:get_ann(origin, Ann, user) of case aeso_syntax:get_ann(origin, Ann, user) of
system -> fresh_uvar(ArgAnn); system -> fresh_uvar(ArgAnn);
@ -1772,8 +1820,8 @@ lookup_name(Env = #env{ namespace = NS, current_function = CurFn }, As, Id, Opti
Freshen = proplists:get_value(freshen, Options, false), Freshen = proplists:get_value(freshen, Options, false),
check_stateful(Env, Id, Ty), check_stateful(Env, Id, Ty),
Ty1 = case Ty of Ty1 = case Ty of
{type_sig, _, _, _, _, _} -> freshen_type_sig(As, Ty); {type_sig, _, _, _, _, _} -> freshen_type_sig(As, Ty, [{fun_name, Id}]);
_ when Freshen -> freshen_type(As, Ty); _ when Freshen -> freshen_type(As, Ty, [{fun_name, Id}]);
_ -> Ty _ -> Ty
end, end,
{set_qname(QId, Id), Ty1} {set_qname(QId, Id), Ty1}
@ -1907,6 +1955,8 @@ infer_expr(_Env, Body={bytes, As, Bin}) ->
{typed, As, Body, {bytes_t, As, byte_size(Bin)}}; {typed, As, Body, {bytes_t, As, byte_size(Bin)}};
infer_expr(_Env, Body={account_pubkey, As, _}) -> infer_expr(_Env, Body={account_pubkey, As, _}) ->
{typed, As, Body, {id, As, "address"}}; {typed, As, Body, {id, As, "address"}};
infer_expr(_Env, Body={signature, As, Bin}) when byte_size(Bin) == 64 ->
{typed, As, Body, {bytes_t, As, 64}};
infer_expr(_Env, Body={oracle_pubkey, As, _}) -> infer_expr(_Env, Body={oracle_pubkey, As, _}) ->
Q = fresh_uvar(As), Q = fresh_uvar(As),
R = fresh_uvar(As), R = fresh_uvar(As),
@ -2015,7 +2065,7 @@ infer_expr(Env, {app, Ann, Fun, Args0} = App) ->
unify(Env, FunType, {fun_t, [], NamedArgsVar, ArgTypes, GeneralResultType}, When), unify(Env, FunType, {fun_t, [], NamedArgsVar, ArgTypes, GeneralResultType}, When),
when_warning(warn_negative_spend, fun() -> warn_potential_negative_spend(Ann, NewFun1, NewArgs) end), when_warning(warn_negative_spend, fun() -> warn_potential_negative_spend(Ann, NewFun1, NewArgs) end),
[ add_constraint({aens_resolve_type, GeneralResultType}) [ add_constraint({aens_resolve_type, GeneralResultType})
|| element(3, FunName) =:= ["AENS", "resolve"] ], || element(3, FunName) =:= ["AENSv2", "resolve"] ],
[ add_constraint({oracle_type, Ann, OType}) [ add_constraint({oracle_type, Ann, OType})
|| OType <- [get_oracle_type(FunName, ArgTypes, GeneralResultType)], || OType <- [get_oracle_type(FunName, ArgTypes, GeneralResultType)],
OType =/= false ], OType =/= false ],
@ -2145,6 +2195,8 @@ check_valid_const_expr({bytes, _, _}) ->
true; true;
check_valid_const_expr({account_pubkey, _, _}) -> check_valid_const_expr({account_pubkey, _, _}) ->
true; true;
check_valid_const_expr({signature, _, _}) ->
true;
check_valid_const_expr({oracle_pubkey, _, _}) -> check_valid_const_expr({oracle_pubkey, _, _}) ->
true; true;
check_valid_const_expr({oracle_query_id, _, _}) -> check_valid_const_expr({oracle_query_id, _, _}) ->
@ -2395,6 +2447,11 @@ infer_infix({IntOp, As})
IntOp == '^'; IntOp == 'mod' -> IntOp == '^'; IntOp == 'mod' ->
Int = {id, As, "int"}, Int = {id, As, "int"},
{fun_t, As, [], [Int, Int], Int}; {fun_t, As, [], [Int, Int], Int};
infer_infix({BitOp, As})
when BitOp == 'band'; BitOp == 'bor'; BitOp == 'bxor';
BitOp == '<<'; BitOp == '>>' ->
Int = {id, As, "int"},
{fun_t, As, [], [Int, Int], Int};
infer_infix({RelOp, As}) infer_infix({RelOp, As})
when RelOp == '=='; RelOp == '!='; when RelOp == '=='; RelOp == '!=';
RelOp == '<'; RelOp == '>'; RelOp == '<'; RelOp == '>';
@ -2422,6 +2479,9 @@ infer_infix({'|>', As}) ->
infer_prefix({'!',As}) -> infer_prefix({'!',As}) ->
Bool = {id, As, "bool"}, Bool = {id, As, "bool"},
{fun_t, As, [], [Bool], Bool}; {fun_t, As, [], [Bool], Bool};
infer_prefix({BitOp,As}) when BitOp =:= 'bnot' ->
Int = {id, As, "int"},
{fun_t, As, [], [Int], Int};
infer_prefix({IntOp,As}) when IntOp =:= '-' -> infer_prefix({IntOp,As}) when IntOp =:= '-' ->
Int = {id, As, "int"}, Int = {id, As, "int"},
{fun_t, As, [], [Int], Int}. {fun_t, As, [], [Int], Int}.
@ -2574,61 +2634,124 @@ get_constraints() ->
destroy_constraints() -> destroy_constraints() ->
ets_delete(constraints). ets_delete(constraints).
-spec solve_constraints(env()) -> ok. %% Solve all constraints by iterating until no-progress
solve_constraints(Env) ->
%% First look for record fields that appear in only one type definition
IsAmbiguous =
fun(#field_constraint{
record_t = RecordType,
field = Field={id, _Attrs, FieldName},
field_t = FieldType,
kind = Kind,
context = When }) ->
Arity = fun_arity(dereference_deep(FieldType)),
FieldInfos = case Arity of
none -> lookup_record_field(Env, FieldName, Kind);
_ -> lookup_record_field_arity(Env, FieldName, Arity, Kind)
end,
case FieldInfos of
[] ->
type_error({undefined_field, Field}),
false;
[#field_info{field_t = FldType, record_t = RecType}] ->
create_freshen_tvars(),
FreshFldType = freshen(FldType),
FreshRecType = freshen(RecType),
destroy_freshen_tvars(),
unify(Env, FreshFldType, FieldType, {field_constraint, FreshFldType, FieldType, When}),
unify(Env, FreshRecType, RecordType, {record_constraint, FreshRecType, RecordType, When}),
false;
_ ->
%% ambiguity--need cleverer strategy
true
end;
(_) -> true
end,
AmbiguousConstraints = lists:filter(IsAmbiguous, get_constraints()),
% The two passes on AmbiguousConstraints are needed -spec solve_all_constraints(env()) -> ok.
solve_ambiguous_constraints(Env, AmbiguousConstraints ++ AmbiguousConstraints). solve_all_constraints(Env) ->
Constraints = [C || C <- get_constraints(), not one_shot_field_constraint(Env, C) ],
solve_constraints_top(Env, Constraints).
-spec solve_ambiguous_constraints(env(), [constraint()]) -> ok. solve_constraints_top(Env, Constraints) ->
solve_ambiguous_constraints(Env, Constraints) -> UnsolvedCs = solve_constraints(Env, Constraints),
Unknown = solve_known_record_types(Env, Constraints), Progress = solve_unknown_record_constraints(Env, UnsolvedCs),
if Unknown == [] -> ok;
length(Unknown) < length(Constraints) -> if length(UnsolvedCs) < length(Constraints) orelse Progress == true ->
%% progress! Keep trying. solve_constraints_top(Env, UnsolvedCs);
solve_ambiguous_constraints(Env, Unknown);
true -> true ->
case solve_unknown_record_types(Env, Unknown) of ok
true -> %% Progress!
solve_ambiguous_constraints(Env, Unknown);
_ -> ok %% No progress. Report errors later.
end
end. end.
-spec solve_constraints(env(), [constraint()]) -> [constraint()].
solve_constraints(Env, Constraints) ->
[ C1 || C <- Constraints, C1 <- [dereference_deep(C)], not solve_constraint(Env, C1) ].
solve_unknown_record_constraints(Env, Constraints) ->
FieldCs = lists:filter(fun(#field_constraint{record_t = {uvar, _, _}}) -> true; (_) -> false end, Constraints),
FieldCsUVars = lists:usort([UVar || #field_constraint{record_t = UVar = {uvar, _, _}} <- FieldCs]),
FieldConstraint = fun(#field_constraint{ field = F, kind = K, context = Ctx }) -> {K, Ctx, F} end,
FieldsForUVar = fun(UVar) ->
[ FieldConstraint(FC) || FC = #field_constraint{record_t = U} <- FieldCs, U == UVar ]
end,
Solutions = [ solve_for_uvar(Env, UVar, FieldsForUVar(UVar)) || UVar <- FieldCsUVars ],
case lists:member(true, Solutions) of
true -> true;
false -> Solutions
end.
%% -- Simple constraints --
%% Returns true if solved (unified or type error)
solve_constraint(_Env, #field_constraint{record_t = {uvar, _, _}}) ->
false;
solve_constraint(Env, #field_constraint{record_t = RecordType,
field = Field = {id, _As, FieldName},
field_t = FieldType,
context = When}) ->
RecId = record_type_name(RecordType),
Attrs = aeso_syntax:get_ann(RecId),
case lookup_type(Env, RecId) of
{_, {_Ann, {Formals, {What, Fields}}}} when What =:= record_t; What =:= contract_t ->
FieldTypes = [{Name, Type} || {field_t, _, {id, _, Name}, Type} <- Fields],
case proplists:get_value(FieldName, FieldTypes) of
undefined ->
type_error({missing_field, Field, RecId});
FldType ->
solve_field_constraint(Env, FieldType, FldType, RecordType, app_t(Attrs, RecId, Formals), When)
end;
_ ->
type_error({not_a_record_type, instantiate(RecordType), When})
end,
true;
solve_constraint(Env, C = #dependent_type_constraint{}) ->
check_named_argument_constraint(Env, C);
solve_constraint(Env, C = #named_argument_constraint{}) ->
check_named_argument_constraint(Env, C);
solve_constraint(_Env, {is_bytes, _, _}) -> false;
solve_constraint(_Env, {is_fixed_bytes, _, _}) -> false;
solve_constraint(Env, {add_bytes, Ann, Action, A0, B0, C0}) ->
A = unfold_types_in_type(Env, dereference(A0)),
B = unfold_types_in_type(Env, dereference(B0)),
C = unfold_types_in_type(Env, dereference(C0)),
case {A, B, C} of
{{bytes_t, _, M}, {bytes_t, _, N}, _} when is_integer(M), is_integer(N) ->
unify(Env, {bytes_t, Ann, M + N}, C, {at, Ann});
{{bytes_t, _, M}, _, {bytes_t, _, R}} when is_integer(M), is_integer(R), R >= M ->
unify(Env, {bytes_t, Ann, R - M}, B, {at, Ann});
{_, {bytes_t, _, N}, {bytes_t, _, R}} when is_integer(N), is_integer(R), R >= N ->
unify(Env, {bytes_t, Ann, R - N}, A, {at, Ann});
{{bytes_t, _, _}, {bytes_t, _, _}, _} when Action == concat ->
unify(Env, {bytes_t, Ann, any}, C, {at, Ann});
_ -> false
end;
solve_constraint(_, _) -> false.
one_shot_field_constraint(Env, #field_constraint{record_t = RecordType,
field = Field = {id, _As, FieldName},
field_t = FieldType,
kind = Kind,
context = When}) ->
Arity = fun_arity(dereference_deep(FieldType)),
FieldInfos = case Arity of
none -> lookup_record_field(Env, FieldName, Kind);
_ -> lookup_record_field_arity(Env, FieldName, Arity, Kind)
end,
case FieldInfos of
[] ->
type_error({undefined_field, Field}),
true;
[#field_info{field_t = FldType, record_t = RecType}] ->
solve_field_constraint(Env, FieldType, FldType, RecordType, RecType, When),
true;
_ ->
false
end;
one_shot_field_constraint(_Env, _Constraint) ->
false.
solve_field_constraint(Env, FieldType, FldType, RecordType, RecType, When) ->
create_freshen_tvars(),
FreshFldType = freshen(FldType),
FreshRecType = freshen(RecType),
destroy_freshen_tvars(),
unify(Env, FreshFldType, FieldType, {field_constraint, FreshFldType, FieldType, When}),
unify(Env, FreshRecType, RecordType, {record_constraint, FreshRecType, RecordType, When}).
solve_then_destroy_and_report_unsolved_constraints(Env) -> solve_then_destroy_and_report_unsolved_constraints(Env) ->
solve_constraints(Env), solve_all_constraints(Env),
destroy_and_report_unsolved_constraints(Env). destroy_and_report_unsolved_constraints(Env).
destroy_and_report_unsolved_constraints(Env) -> destroy_and_report_unsolved_constraints(Env) ->
@ -2646,7 +2769,8 @@ destroy_and_report_unsolved_constraints(Env) ->
(_) -> false (_) -> false
end, OtherCs2), end, OtherCs2),
{BytesCs, OtherCs4} = {BytesCs, OtherCs4} =
lists:partition(fun({is_bytes, _}) -> true; lists:partition(fun({is_bytes, _, _}) -> true;
({is_fixed_bytes, _, _}) -> true;
({add_bytes, _, _, _, _, _}) -> true; ({add_bytes, _, _, _, _, _}) -> true;
(_) -> false (_) -> false
end, OtherCs3), end, OtherCs3),
@ -2659,21 +2783,10 @@ destroy_and_report_unsolved_constraints(Env) ->
(_) -> false (_) -> false
end, OtherCs5), end, OtherCs5),
Unsolved = [ S || S <- [ solve_constraint(Env, dereference_deep(C)) || C <- NamedArgCs ], check_field_constraints(Env, FieldCs),
S == unsolved ],
[ type_error({unsolved_named_argument_constraint, C}) || C <- Unsolved ],
Unknown = solve_known_record_types(Env, FieldCs),
if Unknown == [] -> ok;
true ->
case solve_unknown_record_types(Env, Unknown) of
true -> ok;
Errors -> [ type_error(Err) || Err <- Errors ]
end
end,
check_record_create_constraints(Env, CreateCs), check_record_create_constraints(Env, CreateCs),
check_is_contract_constraints(Env, ContractCs), check_is_contract_constraints(Env, ContractCs),
check_named_args_constraints(Env, NamedArgCs),
check_bytes_constraints(Env, BytesCs), check_bytes_constraints(Env, BytesCs),
check_aens_resolve_constraints(Env, AensResolveCs), check_aens_resolve_constraints(Env, AensResolveCs),
check_oracle_type_constraints(Env, OracleTypeCs), check_oracle_type_constraints(Env, OracleTypeCs),
@ -2691,20 +2804,21 @@ get_oracle_type(_Fun, _Args, _Ret) -> false.
%% -- Named argument constraints -- %% -- Named argument constraints --
%% If false, a type error has been emitted, so it's safe to drop the constraint. %% True if solved (unified or type error), false otherwise
-spec check_named_argument_constraint(env(), named_argument_constraint()) -> true | false | unsolved. -spec check_named_argument_constraint(env(), named_argument_constraint()) -> true | false.
check_named_argument_constraint(_Env, #named_argument_constraint{ args = {uvar, _, _} }) -> check_named_argument_constraint(_Env, #named_argument_constraint{ args = {uvar, _, _} }) ->
unsolved; false;
check_named_argument_constraint(Env, check_named_argument_constraint(Env,
C = #named_argument_constraint{ args = Args, C = #named_argument_constraint{ args = Args,
name = Id = {id, _, Name}, name = Id = {id, _, Name},
type = Type }) -> type = Type }) ->
case [ T || {named_arg_t, _, {id, _, Name1}, T, _} <- Args, Name1 == Name ] of case [ T || {named_arg_t, _, {id, _, Name1}, T, _} <- Args, Name1 == Name ] of
[] -> [] ->
type_error({bad_named_argument, Args, Id}), type_error({bad_named_argument, Args, Id});
false; [T] ->
[T] -> unify(Env, T, Type, {check_named_arg_constraint, C}), true unify(Env, T, Type, {check_named_arg_constraint, C})
end; end,
true;
check_named_argument_constraint(Env, check_named_argument_constraint(Env,
#dependent_type_constraint{ named_args_t = NamedArgsT0, #dependent_type_constraint{ named_args_t = NamedArgsT0,
named_args = NamedArgs, named_args = NamedArgs,
@ -2721,10 +2835,11 @@ check_named_argument_constraint(Env,
ArgEnv = maps:from_list([ {Name, GetVal(Name, Default)} ArgEnv = maps:from_list([ {Name, GetVal(Name, Default)}
|| {named_arg_t, _, {id, _, Name}, _, Default} <- NamedArgsT ]), || {named_arg_t, _, {id, _, Name}, _, Default} <- NamedArgsT ]),
GenType1 = specialize_dependent_type(ArgEnv, GenType), GenType1 = specialize_dependent_type(ArgEnv, GenType),
unify(Env, GenType1, SpecType, {check_expr, App, GenType1, SpecType}), unify(Env, GenType1, SpecType, {check_expr, App, GenType1, SpecType});
true; _ ->
_ -> unify(Env, GenType, SpecType, {check_expr, App, GenType, SpecType}), true unify(Env, GenType, SpecType, {check_expr, App, GenType, SpecType})
end. end,
true.
specialize_dependent_type(Env, Type) -> specialize_dependent_type(Env, Type) ->
case dereference(Type) of case dereference(Type) of
@ -2740,70 +2855,44 @@ specialize_dependent_type(Env, Type) ->
_ -> Type %% Currently no deep dependent types _ -> Type %% Currently no deep dependent types
end. end.
%% -- Bytes constraints -- check_field_constraints(Env, Constraints) ->
UnsolvedFieldCs = solve_constraints(Env, Constraints),
case solve_unknown_record_constraints(Env, UnsolvedFieldCs) of
true -> ok;
Errors -> [ type_error(Err) || Err <- Errors ]
end.
solve_constraint(_Env, #field_constraint{record_t = {uvar, _, _}}) -> check_named_args_constraints(Env, Constraints) ->
not_solved; UnsolvedNamedArgCs = solve_constraints(Env, Constraints),
solve_constraint(Env, C = #field_constraint{record_t = RecType, [ type_error({unsolved_named_argument_constraint, C}) || C <- UnsolvedNamedArgCs ].
field = FieldName,
field_t = FieldType,
context = When}) ->
RecId = record_type_name(RecType),
Attrs = aeso_syntax:get_ann(RecId),
case lookup_type(Env, RecId) of
{_, {_Ann, {Formals, {What, Fields}}}} when What =:= record_t; What =:= contract_t ->
FieldTypes = [{Name, Type} || {field_t, _, {id, _, Name}, Type} <- Fields],
{id, _, FieldString} = FieldName,
case proplists:get_value(FieldString, FieldTypes) of
undefined ->
type_error({missing_field, FieldName, RecId}),
not_solved;
FldType ->
create_freshen_tvars(),
FreshFldType = freshen(FldType),
FreshRecType = freshen(app_t(Attrs, RecId, Formals)),
destroy_freshen_tvars(),
unify(Env, FreshFldType, FieldType, {field_constraint, FreshFldType, FieldType, When}),
unify(Env, FreshRecType, RecType, {record_constraint, FreshRecType, RecType, When}),
C
end;
_ ->
type_error({not_a_record_type, instantiate(RecType), When}),
not_solved
end;
solve_constraint(Env, C = #dependent_type_constraint{}) ->
check_named_argument_constraint(Env, C);
solve_constraint(Env, C = #named_argument_constraint{}) ->
check_named_argument_constraint(Env, C);
solve_constraint(_Env, {is_bytes, _}) -> ok;
solve_constraint(Env, {add_bytes, Ann, _, A0, B0, C0}) ->
A = unfold_types_in_type(Env, dereference(A0)),
B = unfold_types_in_type(Env, dereference(B0)),
C = unfold_types_in_type(Env, dereference(C0)),
case {A, B, C} of
{{bytes_t, _, M}, {bytes_t, _, N}, _} -> unify(Env, {bytes_t, Ann, M + N}, C, {at, Ann});
{{bytes_t, _, M}, _, {bytes_t, _, R}} when R >= M -> unify(Env, {bytes_t, Ann, R - M}, B, {at, Ann});
{_, {bytes_t, _, N}, {bytes_t, _, R}} when R >= N -> unify(Env, {bytes_t, Ann, R - N}, A, {at, Ann});
_ -> ok
end;
solve_constraint(_, _) -> ok.
check_bytes_constraints(Env, Constraints) -> check_bytes_constraints(Env, Constraints) ->
InAddConstraint = [ T || {add_bytes, _, _, A, B, C} <- Constraints, InAddConstraint = [ T || {add_bytes, _, _, A, B, C} <- Constraints,
T <- [A, B, C], T <- [A, B, C],
element(1, T) /= bytes_t ], element(1, T) /= bytes_t ],
InSplitConstraint = [ T || {add_bytes, _, split, A, B, C} <- Constraints,
T <- [A, B, C],
element(1, T) /= bytes_t ],
%% Skip is_bytes constraints for types that occur in add_bytes constraints %% Skip is_bytes constraints for types that occur in add_bytes constraints
%% (no need to generate error messages for both is_bytes and add_bytes). %% (no need to generate error messages for both is_bytes and add_bytes).
Skip = fun({is_bytes, T}) -> lists:member(T, InAddConstraint); Skip = fun({is_bytes, _, T}) -> lists:member(T, InAddConstraint);
({is_fixed_bytes, _, T}) -> lists:member(T, InSplitConstraint);
(_) -> false end, (_) -> false end,
[ check_bytes_constraint(Env, C) || C <- Constraints, not Skip(C) ]. [ check_bytes_constraint(Env, C) || C <- Constraints, not Skip(C) ].
check_bytes_constraint(Env, {is_bytes, Type}) -> check_bytes_constraint(Env, {is_bytes, Ann, Type}) ->
Type1 = unfold_types_in_type(Env, instantiate(Type)), Type1 = unfold_types_in_type(Env, instantiate(Type)),
case Type1 of case Type1 of
{bytes_t, _, _} -> ok; {bytes_t, _, N} when is_integer(N); N == any -> ok;
_ -> _ ->
type_error({unknown_byte_length, Type}) type_error({unknown_byte_type, Ann, Type})
end;
check_bytes_constraint(Env, {is_fixed_bytes, Ann, Type}) ->
Type1 = unfold_types_in_type(Env, instantiate(Type)),
case Type1 of
{bytes_t, _, N} when is_integer(N) -> ok;
_ ->
type_error({unknown_byte_length, Ann, Type})
end; end;
check_bytes_constraint(Env, {add_bytes, Ann, Fun, A0, B0, C0}) -> check_bytes_constraint(Env, {add_bytes, Ann, Fun, A0, B0, C0}) ->
A = unfold_types_in_type(Env, instantiate(A0)), A = unfold_types_in_type(Env, instantiate(A0)),
@ -2883,36 +2972,11 @@ check_is_contract_constraints(Env, [C | Cs]) ->
end, end,
check_is_contract_constraints(Env, Cs). check_is_contract_constraints(Env, Cs).
-spec solve_unknown_record_types(env(), [field_constraint()]) -> true | [tuple()].
solve_unknown_record_types(Env, Unknown) ->
UVars = lists:usort([UVar || #field_constraint{record_t = UVar = {uvar, _, _}} <- Unknown]),
Solutions = [solve_for_uvar(Env, UVar, [{Kind, When, Field}
|| #field_constraint{record_t = U, field = Field, kind = Kind, context = When} <- Unknown,
U == UVar])
|| UVar <- UVars],
case lists:member(true, Solutions) of
true -> true;
false -> Solutions
end.
%% This will solve all kinds of constraints but will only return the
%% unsolved field constraints
-spec solve_known_record_types(env(), [constraint()]) -> [field_constraint()].
solve_known_record_types(Env, Constraints) ->
DerefConstraints = lists:map(fun(C = #field_constraint{record_t = RecordType}) ->
C#field_constraint{record_t = dereference(RecordType)};
(C) -> dereference_deep(C)
end, Constraints),
SolvedConstraints = lists:map(fun(C) -> solve_constraint(Env, dereference_deep(C)) end, DerefConstraints),
Unsolved = DerefConstraints--SolvedConstraints,
lists:filter(fun(#field_constraint{}) -> true; (_) -> false end, Unsolved).
record_type_name({app_t, _Attrs, RecId, _Args}) when ?is_type_id(RecId) -> record_type_name({app_t, _Attrs, RecId, _Args}) when ?is_type_id(RecId) ->
RecId; RecId;
record_type_name(RecId) when ?is_type_id(RecId) -> record_type_name(RecId) when ?is_type_id(RecId) ->
RecId; RecId;
record_type_name(_Other) -> record_type_name(_Other) ->
%% io:format("~p is not a record type\n", [Other]),
{id, [{origin, system}], "not_a_record_type"}. {id, [{origin, system}], "not_a_record_type"}.
solve_for_uvar(Env, UVar = {uvar, Attrs, _}, Fields0) -> solve_for_uvar(Env, UVar = {uvar, Attrs, _}, Fields0) ->
@ -3020,7 +3084,8 @@ unfold_types_in_type(Env, {app_t, Ann, Id, Args}, Options) when ?is_type_id(Id)
unfold_types_in_type(Env, Id, Options) when ?is_type_id(Id) -> unfold_types_in_type(Env, Id, Options) when ?is_type_id(Id) ->
%% Like the case above, but for types without parameters. %% Like the case above, but for types without parameters.
when_warning(warn_unused_typedefs, fun() -> used_typedef(Id, 0) end), when_warning(warn_unused_typedefs, fun() -> used_typedef(Id, 0) end),
UnfoldRecords = proplists:get_value(unfold_record_types, Options, false), UnfoldSysAlias = not proplists:get_value(not_unfold_system_alias_types, Options, false),
UnfoldRecords = proplists:get_value(unfold_record_types, Options, false),
UnfoldVariants = proplists:get_value(unfold_variant_types, Options, false), UnfoldVariants = proplists:get_value(unfold_variant_types, Options, false),
case lookup_type(Env, Id) of case lookup_type(Env, Id) of
{_, {_, {[], {record_t, Fields}}}} when UnfoldRecords -> {_, {_, {[], {record_t, Fields}}}} when UnfoldRecords ->
@ -3028,7 +3093,12 @@ unfold_types_in_type(Env, Id, Options) when ?is_type_id(Id) ->
{_, {_, {[], {variant_t, Constrs}}}} when UnfoldVariants -> {_, {_, {[], {variant_t, Constrs}}}} when UnfoldVariants ->
{variant_t, unfold_types_in_type(Env, Constrs, Options)}; {variant_t, unfold_types_in_type(Env, Constrs, Options)};
{_, {_, {[], {alias_t, Type1}}}} -> {_, {_, {[], {alias_t, Type1}}}} ->
unfold_types_in_type(Env, Type1, Options); case aeso_syntax:get_ann(Type1) of
[{origin, system}] when not UnfoldSysAlias ->
Id;
_ ->
unfold_types_in_type(Env, Type1, Options)
end;
_ -> _ ->
%% Not a record type, or ill-formed record type %% Not a record type, or ill-formed record type
Id Id
@ -3085,16 +3155,12 @@ unify0(Env, A, B, Variance, When) ->
unify1(_Env, {uvar, _, R}, {uvar, _, R}, _Variance, _When) -> unify1(_Env, {uvar, _, R}, {uvar, _, R}, _Variance, _When) ->
true; true;
unify1(_Env, {uvar, _, _}, {fun_t, _, _, var_args, _}, _Variance, When) -> unify1(_Env, {uvar, _, _}, {fun_t, _, _, var_args, _}, _Variance, When) ->
type_error({unify_varargs, When}); type_error({unify_varargs, When}),
unify1(Env, {uvar, A, R}, T, _Variance, When) -> false;
unify1(_Env, {uvar, A, R}, T, _Variance, When) ->
case occurs_check(R, T) of case occurs_check(R, T) of
true -> true ->
if cannot_unify({uvar, A, R}, T, none, When),
Env#env.unify_throws ->
cannot_unify({uvar, A, R}, T, none, When);
true ->
ok
end,
false; false;
false -> false ->
ets_insert(type_vars, {R, T}), ets_insert(type_vars, {R, T}),
@ -3104,9 +3170,9 @@ unify1(Env, T, {uvar, A, R}, Variance, When) ->
unify1(Env, {uvar, A, R}, T, Variance, When); unify1(Env, {uvar, A, R}, T, Variance, When);
unify1(_Env, {tvar, _, X}, {tvar, _, X}, _Variance, _When) -> true; %% Rigid type variables unify1(_Env, {tvar, _, X}, {tvar, _, X}, _Variance, _When) -> true; %% Rigid type variables
unify1(Env, [A|B], [C|D], [V|Variances], When) -> unify1(Env, [A|B], [C|D], [V|Variances], When) ->
unify0(Env, A, C, V, When) andalso unify0(Env, B, D, Variances, When); unify0(Env, A, C, V, When) and unify0(Env, B, D, Variances, When);
unify1(Env, [A|B], [C|D], Variance, When) -> unify1(Env, [A|B], [C|D], Variance, When) ->
unify0(Env, A, C, Variance, When) andalso unify0(Env, B, D, Variance, When); unify0(Env, A, C, Variance, When) and unify0(Env, B, D, Variance, When);
unify1(_Env, X, X, _Variance, _When) -> unify1(_Env, X, X, _Variance, _When) ->
true; true;
unify1(_Env, _A, {id, _, "void"}, Variance, _When) unify1(_Env, _A, {id, _, "void"}, Variance, _When)
@ -3121,18 +3187,13 @@ unify1(Env, A = {con, _, NameA}, B = {con, _, NameB}, Variance, When) ->
case is_subtype(Env, NameA, NameB, Variance) of case is_subtype(Env, NameA, NameB, Variance) of
true -> true; true -> true;
false -> false ->
if IsSubtype = is_subtype(Env, NameA, NameB, contravariant) orelse
Env#env.unify_throws -> is_subtype(Env, NameA, NameB, covariant),
IsSubtype = is_subtype(Env, NameA, NameB, contravariant) orelse Cxt = case IsSubtype of
is_subtype(Env, NameA, NameB, covariant), true -> Variance;
Cxt = case IsSubtype of false -> none
true -> Variance; end,
false -> none cannot_unify(A, B, Cxt, When),
end,
cannot_unify(A, B, Cxt, When);
true ->
ok
end,
false false
end; end;
unify1(_Env, {qid, _, Name}, {qid, _, Name}, _Variance, _When) -> unify1(_Env, {qid, _, Name}, {qid, _, Name}, _Variance, _When) ->
@ -3146,13 +3207,15 @@ unify1(Env, {if_t, _, {id, _, Id}, Then1, Else1}, {if_t, _, {id, _, Id}, Then2,
unify0(Env, Else1, Else2, Variance, When); unify0(Env, Else1, Else2, Variance, When);
unify1(_Env, {fun_t, _, _, _, _}, {fun_t, _, _, var_args, _}, _Variance, When) -> unify1(_Env, {fun_t, _, _, _, _}, {fun_t, _, _, var_args, _}, _Variance, When) ->
type_error({unify_varargs, When}); type_error({unify_varargs, When}),
false;
unify1(_Env, {fun_t, _, _, var_args, _}, {fun_t, _, _, _, _}, _Variance, When) -> unify1(_Env, {fun_t, _, _, var_args, _}, {fun_t, _, _, _, _}, _Variance, When) ->
type_error({unify_varargs, When}); type_error({unify_varargs, When}),
false;
unify1(Env, {fun_t, _, Named1, Args1, Result1}, {fun_t, _, Named2, Args2, Result2}, Variance, When) unify1(Env, {fun_t, _, Named1, Args1, Result1}, {fun_t, _, Named2, Args2, Result2}, Variance, When)
when length(Args1) == length(Args2) -> when length(Args1) == length(Args2) ->
unify0(Env, Named1, Named2, opposite_variance(Variance), When) andalso unify0(Env, Named1, Named2, opposite_variance(Variance), When) and
unify0(Env, Args1, Args2, opposite_variance(Variance), When) andalso unify0(Env, Args1, Args2, opposite_variance(Variance), When) and
unify0(Env, Result1, Result2, Variance, When); unify0(Env, Result1, Result2, Variance, When);
unify1(Env, {app_t, _, {Tag, _, F}, Args1}, {app_t, _, {Tag, _, F}, Args2}, Variance, When) unify1(Env, {app_t, _, {Tag, _, F}, Args1}, {app_t, _, {Tag, _, F}, Args2}, Variance, When)
when length(Args1) == length(Args2), Tag == id orelse Tag == qid -> when length(Args1) == length(Args2), Tag == id orelse Tag == qid ->
@ -3170,7 +3233,7 @@ unify1(Env, {tuple_t, _, As}, {tuple_t, _, Bs}, Variance, When)
when length(As) == length(Bs) -> when length(As) == length(Bs) ->
unify0(Env, As, Bs, Variance, When); unify0(Env, As, Bs, Variance, When);
unify1(Env, {named_arg_t, _, Id1, Type1, _}, {named_arg_t, _, Id2, Type2, _}, Variance, When) -> unify1(Env, {named_arg_t, _, Id1, Type1, _}, {named_arg_t, _, Id2, Type2, _}, Variance, When) ->
unify1(Env, Id1, Id2, Variance, {arg_name, Id1, Id2, When}), unify1(Env, Id1, Id2, Variance, {arg_name, Id1, Id2, When}) andalso
unify1(Env, Type1, Type2, Variance, When); unify1(Env, Type1, Type2, Variance, When);
%% The grammar is a bit inconsistent about whether types without %% The grammar is a bit inconsistent about whether types without
%% arguments are represented as applications to an empty list of %% arguments are represented as applications to an empty list of
@ -3179,13 +3242,8 @@ unify1(Env, {app_t, _, T, []}, B, Variance, When) ->
unify0(Env, T, B, Variance, When); unify0(Env, T, B, Variance, When);
unify1(Env, A, {app_t, _, T, []}, Variance, When) -> unify1(Env, A, {app_t, _, T, []}, Variance, When) ->
unify0(Env, A, T, Variance, When); unify0(Env, A, T, Variance, When);
unify1(Env, A, B, _Variance, When) -> unify1(_Env, A, B, _Variance, When) ->
if cannot_unify(A, B, none, When),
Env#env.unify_throws ->
cannot_unify(A, B, none, When);
true ->
ok
end,
false. false.
is_subtype(_Env, NameA, NameB, invariant) -> is_subtype(_Env, NameA, NameB, invariant) ->
@ -3266,49 +3324,59 @@ create_freshen_tvars() ->
destroy_freshen_tvars() -> destroy_freshen_tvars() ->
ets_delete(freshen_tvars). ets_delete(freshen_tvars).
freshen_type(Ann, Type) -> freshen_type(Ann, Type, Ctx) ->
create_freshen_tvars(), create_freshen_tvars(),
Type1 = freshen(Ann, Type), Type1 = freshen(Ann, Type, Ctx),
destroy_freshen_tvars(), destroy_freshen_tvars(),
Type1. Type1.
freshen(Type) -> freshen(Type) ->
freshen(aeso_syntax:get_ann(Type), Type). freshen(aeso_syntax:get_ann(Type), Type, none).
freshen(Ann, {tvar, _, Name}) -> freshen(Ann, {tvar, _, Name}, _Ctx) ->
NewT = case ets_lookup(freshen_tvars, Name) of NewT = case ets_lookup(freshen_tvars, Name) of
[] -> fresh_uvar(Ann); [] -> fresh_uvar(Ann);
[{Name, T}] -> T [{Name, T}] -> T
end, end,
ets_insert(freshen_tvars, {Name, NewT}), ets_insert(freshen_tvars, {Name, NewT}),
NewT; NewT;
freshen(Ann, {bytes_t, _, any}) -> freshen(Ann, {bytes_t, _, '_'}, Ctx) ->
X = fresh_uvar(Ann), X = fresh_uvar(Ann),
add_constraint({is_bytes, X}), add_constraint({is_bytes, Ctx, X}),
X; X;
freshen(Ann, T) when is_tuple(T) -> freshen(Ann, {bytes_t, _, fixed}, Ctx) ->
list_to_tuple(freshen(Ann, tuple_to_list(T))); X = fresh_uvar(Ann),
freshen(Ann, [A | B]) -> add_constraint({is_fixed_bytes, Ctx, X}),
[freshen(Ann, A) | freshen(Ann, B)]; X;
freshen(_, X) -> freshen(Ann, {fun_t, FAnn, NamedArgs, Args, Result}, Ctx) when is_list(Args) ->
{fun_t, FAnn, freshen(Ann, NamedArgs, Ctx),
[ freshen(Ann, Arg, [{arg, Ix} | Ctx]) || {Arg, Ix} <- lists:zip(Args, lists:seq(1, length(Args))) ],
freshen(Ann, Result, [result | Ctx])};
freshen(Ann, {fun_t, FAnn, NamedArgs, Arg, Result}, Ctx) ->
{fun_t, FAnn, freshen(Ann, NamedArgs, Ctx), freshen(Ann, Arg, Ctx), freshen(Ann, Result, [result | Ctx])};
freshen(Ann, T, Ctx) when is_tuple(T) ->
list_to_tuple(freshen(Ann, tuple_to_list(T), Ctx));
freshen(Ann, [A | B], Ctx) ->
[freshen(Ann, A, Ctx) | freshen(Ann, B, Ctx)];
freshen(_, X, _Ctx) ->
X. X.
freshen_type_sig(Ann, TypeSig = {type_sig, _, Constr, _, _, _}) -> freshen_type_sig(Ann, TypeSig = {type_sig, _, Constr, _, _, _}, Ctx) ->
FunT = freshen_type(Ann, typesig_to_fun_t(TypeSig)), FunT = freshen_type(Ann, typesig_to_fun_t(TypeSig), Ctx),
apply_typesig_constraint(Ann, Constr, FunT), apply_typesig_constraint(Ann, Constr, FunT),
FunT. FunT.
apply_typesig_constraint(_Ann, none, _FunT) -> ok; apply_typesig_constraint(_Ann, none, _FunT) -> ok;
apply_typesig_constraint(Ann, address_to_contract, {fun_t, _, [], [_], Type}) -> apply_typesig_constraint(Ann, address_to_contract, {fun_t, _, [], [_], Type}) ->
add_constraint([#is_contract_constraint{ contract_t = Type, add_constraint([#is_contract_constraint{ contract_t = Type,
context = {address_to_contract, Ann}}]); context = {address_to_contract, Ann}}]);
apply_typesig_constraint(Ann, bytes_concat, {fun_t, _, [], [A, B], C}) -> apply_typesig_constraint(Ann, bytes_concat, {fun_t, _, [], [A, B], C}) ->
add_constraint({add_bytes, Ann, concat, A, B, C}); add_constraint({add_bytes, Ann, concat, A, B, C});
apply_typesig_constraint(Ann, bytes_split, {fun_t, _, [], [C], {tuple_t, _, [A, B]}}) -> apply_typesig_constraint(Ann, bytes_split, {fun_t, _, [], [C], {tuple_t, _, [A, B]}}) ->
add_constraint({add_bytes, Ann, split, A, B, C}); add_constraint({add_bytes, Ann, split, A, B, C});
apply_typesig_constraint(Ann, bytecode_hash, {fun_t, _, _, [Con], _}) -> apply_typesig_constraint(Ann, bytecode_hash, {fun_t, _, _, [Con], _}) ->
add_constraint([#is_contract_constraint{ contract_t = Con, add_constraint([#is_contract_constraint{ contract_t = Con,
context = {bytecode_hash, Ann} }]). context = {bytecode_hash, Ann} }]).
%% Dereferences all uvars and replaces the uninstantiated ones with a %% Dereferences all uvars and replaces the uninstantiated ones with a
@ -3341,7 +3409,7 @@ instantiate1(X) ->
integer_to_tvar(X) when X < 26 -> integer_to_tvar(X) when X < 26 ->
[$a + X]; [$a + X];
integer_to_tvar(X) -> integer_to_tvar(X) ->
[integer_to_tvar(X div 26)] ++ [$a + (X rem 26)]. integer_to_tvar(X div 26 - 1) ++ [$a + (X rem 26)].
%% Warnings %% Warnings
@ -3523,7 +3591,6 @@ create_type_errors() ->
destroy_and_report_type_errors(Env) -> destroy_and_report_type_errors(Env) ->
Errors0 = lists:reverse(ets_tab2list(type_errors)), Errors0 = lists:reverse(ets_tab2list(type_errors)),
%% io:format("Type errors now: ~p\n", [Errors0]),
ets_delete(type_errors), ets_delete(type_errors),
Errors = [ mk_error(unqualify(Env, Err)) || Err <- Errors0 ], Errors = [ mk_error(unqualify(Env, Err)) || Err <- Errors0 ],
aeso_errors:throw(Errors). %% No-op if Errors == [] aeso_errors:throw(Errors). %% No-op if Errors == []
@ -3729,6 +3796,9 @@ mk_error({type_decl, _, {id, Pos, Name}, _}) ->
Msg = io_lib:format("Empty type declarations are not supported. Type `~s` lacks a definition", Msg = io_lib:format("Empty type declarations are not supported. Type `~s` lacks a definition",
[Name]), [Name]),
mk_t_err(pos(Pos), Msg); mk_t_err(pos(Pos), Msg);
mk_error({too_many_tvars, Name, {type_sig, Pos, _, _, _, _}}) ->
Msg = io_lib:format("Too many type variables (max 256) in definition of `~s`", [Name]),
mk_t_err(pos(Pos), Msg);
mk_error({stateful_not_allowed, Id, Fun}) -> mk_error({stateful_not_allowed, Id, Fun}) ->
Msg = io_lib:format("Cannot reference stateful function `~s` in the definition of non-stateful function `~s`.", Msg = io_lib:format("Cannot reference stateful function `~s` in the definition of non-stateful function `~s`.",
[pp(Id), pp(Fun)]), [pp(Id), pp(Fun)]),
@ -3829,8 +3899,11 @@ mk_error({bad_top_level_decl, Decl}) ->
Msg = io_lib:format("The definition of '~s' must appear inside a ~s.", Msg = io_lib:format("The definition of '~s' must appear inside a ~s.",
[pp_expr(Id), What]), [pp_expr(Id), What]),
mk_t_err(pos(Decl), Msg); mk_t_err(pos(Decl), Msg);
mk_error({unknown_byte_length, Type}) -> mk_error({unknown_byte_type, Ctx, Type}) ->
Msg = io_lib:format("Cannot resolve length of byte array.", []), Msg = io_lib:format("Cannot resolve type of byte array in\n ~s", [pp_context(Ctx)]),
mk_t_err(pos(Type), Msg);
mk_error({unknown_byte_length, Ctx, Type}) ->
Msg = io_lib:format("Cannot resolve length of byte array in\n ~s", [pp_context(Ctx)]),
mk_t_err(pos(Type), Msg); mk_t_err(pos(Type), Msg);
mk_error({unsolved_bytes_constraint, Ann, concat, A, B, C}) -> mk_error({unsolved_bytes_constraint, Ann, concat, A, B, C}) ->
Msg = io_lib:format("Failed to resolve byte array lengths in call to Bytes.concat with arguments of type\n" Msg = io_lib:format("Failed to resolve byte array lengths in call to Bytes.concat with arguments of type\n"
@ -3844,6 +3917,12 @@ mk_error({unsolved_bytes_constraint, Ann, split, A, B, C}) ->
[ pp_type(" - ", C), pp_loc(C), pp_type(" - ", A), pp_loc(A), [ pp_type(" - ", C), pp_loc(C), pp_type(" - ", A), pp_loc(A),
pp_type(" - ", B), pp_loc(B)]), pp_type(" - ", B), pp_loc(B)]),
mk_t_err(pos(Ann), Msg); mk_t_err(pos(Ann), Msg);
mk_error({unsolved_bytes_constraint, Ann, split_any, A, B, C}) ->
Msg = io_lib:format("Failed to resolve byte arrays in call to Bytes.split_any with argument of type\n"
"~s (at ~s)\nand result types\n~s (at ~s)\n~s (at ~s)",
[ pp_type(" - ", C), pp_loc(C), pp_type(" - ", A), pp_loc(A),
pp_type(" - ", B), pp_loc(B)]),
mk_t_err(pos(Ann), Msg);
mk_error({failed_to_get_compiler_version, Err}) -> mk_error({failed_to_get_compiler_version, Err}) ->
Msg = io_lib:format("Failed to get compiler version. Error: ~p", [Err]), Msg = io_lib:format("Failed to get compiler version. Error: ~p", [Err]),
mk_t_err(pos(0, 0), Msg); mk_t_err(pos(0, 0), Msg);
@ -3941,7 +4020,7 @@ mk_error({higher_order_entrypoint, Ann, {id, _, Name}, Thing}) ->
[ThingS, Name, Bad]), [ThingS, Name, Bad]),
mk_t_err(pos(Ann), Msg); mk_t_err(pos(Ann), Msg);
mk_error({invalid_aens_resolve_type, Ann, T}) -> mk_error({invalid_aens_resolve_type, Ann, T}) ->
Msg = io_lib:format("Invalid return type of `AENS.resolve`:\n" Msg = io_lib:format("Invalid return type of `AENSv2.resolve`:\n"
"~s`\n" "~s`\n"
"It must be a `string` or a pubkey type (`address`, `oracle`, etc)", "It must be a `string` or a pubkey type (`address`, `oracle`, etc)",
[pp_type(" `", T)]), [pp_type(" `", T)]),
@ -4118,8 +4197,8 @@ pp_when({if_branches, Then, ThenType0, Else, ElseType0}) ->
Branches = [ {Then, ThenType} | [ {B, ElseType} || B <- if_branches(Else) ] ], Branches = [ {Then, ThenType} | [ {B, ElseType} || B <- if_branches(Else) ] ],
{pos(element(1, hd(Branches))), {pos(element(1, hd(Branches))),
io_lib:format("when comparing the types of the if-branches\n" io_lib:format("when comparing the types of the if-branches\n"
"~s", [ [ io_lib:format("~s (at ~s)\n", [pp_typed(" - ", B, BType), pp_loc(B)]) "~s", [string:join([ io_lib:format("~s (at ~s)", [pp_typed(" - ", B, BType), pp_loc(B)])
|| {B, BType} <- Branches ] ])}; || {B, BType} <- Branches ], "\n")])};
pp_when({case_pat, Pat, PatType0, ExprType0}) -> pp_when({case_pat, Pat, PatType0, ExprType0}) ->
{PatType, ExprType} = instantiate({PatType0, ExprType0}), {PatType, ExprType} = instantiate({PatType0, ExprType0}),
{pos(Pat), {pos(Pat),
@ -4166,6 +4245,10 @@ pp_when({var_args, Ann, Fun}) ->
{pos(Ann) {pos(Ann)
, io_lib:format("when resolving arguments of variadic function `~s`", [pp_expr(Fun)]) , io_lib:format("when resolving arguments of variadic function `~s`", [pp_expr(Fun)])
}; };
pp_when({implement_interface_fun, Ann, Entrypoint, Interface}) ->
{ pos(Ann)
, io_lib:format("when implementing the entrypoint `~s` from the interface `~s`", [Entrypoint, Interface])
};
pp_when(unknown) -> {pos(0,0), ""}. pp_when(unknown) -> {pos(0,0), ""}.
-spec pp_why_record(why_record()) -> {pos(), iolist()}. -spec pp_why_record(why_record()) -> {pos(), iolist()}.
@ -4210,6 +4293,18 @@ pp_type(Type) ->
pp_type(Label, Type) -> pp_type(Label, Type) ->
prettypr:format(prettypr:beside(prettypr:text(Label), aeso_pretty:type(Type, [show_generated])), 80, 80). prettypr:format(prettypr:beside(prettypr:text(Label), aeso_pretty:type(Type, [show_generated])), 80, 80).
pp_context([{fun_name, Id}]) -> ["a call to ", pp(Id)];
pp_context([result | Ctx]) -> ["the result of ", pp_context(Ctx)];
pp_context([{arg, N} | Ctx]) ->
Cnt = fun(1) -> "first";
(2) -> "second";
(3) -> "third";
(I) -> io_lib:format("~pth", [I])
end,
["the ", Cnt(N), " argument of ", pp_context(Ctx)];
pp_context(none) -> "unknown context".
src_file(T) -> aeso_syntax:get_ann(file, T, no_file). src_file(T) -> aeso_syntax:get_ann(file, T, no_file).
include_type(T) -> aeso_syntax:get_ann(include_type, T, none). include_type(T) -> aeso_syntax:get_ann(include_type, T, none).
line_number(T) -> aeso_syntax:get_ann(line, T, 0). line_number(T) -> aeso_syntax:get_ann(line, T, 0).
@ -4261,7 +4356,7 @@ pp({tuple_t, _, []}) ->
"unit"; "unit";
pp({tuple_t, _, Cpts}) -> pp({tuple_t, _, Cpts}) ->
["(", string:join(lists:map(fun pp/1, Cpts), " * "), ")"]; ["(", string:join(lists:map(fun pp/1, Cpts), " * "), ")"];
pp({bytes_t, _, any}) -> "bytes(_)"; pp({bytes_t, _, any}) -> "bytes()";
pp({bytes_t, _, Len}) -> pp({bytes_t, _, Len}) ->
["bytes(", integer_to_list(Len), ")"]; ["bytes(", integer_to_list(Len), ")"];
pp({app_t, _, T, []}) -> pp({app_t, _, T, []}) ->

File diff suppressed because it is too large Load Diff

View File

@ -13,6 +13,8 @@
, file/2 , file/2
, from_string/2 , from_string/2
, check_call/4 , check_call/4
, decode_value/4
, encode_value/4
, create_calldata/3 , create_calldata/3
, create_calldata/4 , create_calldata/4
, version/0 , version/0
@ -41,6 +43,7 @@
| {include, {file_system, [string()]} | | {include, {file_system, [string()]} |
{explicit_files, #{string() => binary()}}} {explicit_files, #{string() => binary()}}}
| {src_file, string()} | {src_file, string()}
| {src_dir, string()}
| {aci, aeso_aci:aci_type()}. | {aci, aeso_aci:aci_type()}.
-type options() :: [option()]. -type options() :: [option()].
@ -86,7 +89,9 @@ file(Filename) ->
file(File, Options0) -> file(File, Options0) ->
Options = add_include_path(File, Options0), Options = add_include_path(File, Options0),
case read_contract(File) of case read_contract(File) of
{ok, Bin} -> from_string(Bin, [{src_file, File} | Options]); {ok, Bin} ->
SrcDir = aeso_utils:canonical_dir(filename:dirname(File)),
from_string(Bin, [{src_file, File}, {src_dir, SrcDir} | Options]);
{error, Error} -> {error, Error} ->
Msg = lists:flatten([File,": ",file:format_error(Error)]), Msg = lists:flatten([File,": ",file:format_error(Error)]),
{error, [aeso_errors:new(file_error, Msg)]} {error, [aeso_errors:new(file_error, Msg)]}
@ -98,7 +103,7 @@ add_include_path(File, Options) ->
false -> false ->
Dir = filename:dirname(File), Dir = filename:dirname(File),
{ok, Cwd} = file:get_cwd(), {ok, Cwd} = file:get_cwd(),
[{include, {file_system, [Cwd, Dir]}} | Options] [{include, {file_system, [Cwd, aeso_utils:canonical_dir(Dir)]}} | Options]
end. end.
-spec from_string(binary() | string(), options()) -> {ok, map()} | {error, [aeso_errors:error()]}. -spec from_string(binary() | string(), options()) -> {ok, map()} | {error, [aeso_errors:error()]}.
@ -118,7 +123,7 @@ from_string1(ContractString, Options) ->
, warnings := Warnings } = string_to_code(ContractString, Options), , warnings := Warnings } = string_to_code(ContractString, Options),
#{ child_con_env := ChildContracts } = FCodeEnv, #{ child_con_env := ChildContracts } = FCodeEnv,
SavedFreshNames = maps:get(saved_fresh_names, FCodeEnv, #{}), SavedFreshNames = maps:get(saved_fresh_names, FCodeEnv, #{}),
{FateCode, VarsRegs} = aeso_fcode_to_fate:compile(ChildContracts, FCode, SavedFreshNames, Options), FateCode = aeso_fcode_to_fate:compile(ChildContracts, FCode, SavedFreshNames, Options),
pp_assembler(FateCode, Options), pp_assembler(FateCode, Options),
ByteCode = aeb_fate_code:serialize(FateCode, []), ByteCode = aeb_fate_code:serialize(FateCode, []),
{ok, Version} = version(), {ok, Version} = version(),
@ -131,13 +136,7 @@ from_string1(ContractString, Options) ->
payable => maps:get(payable, FCode), payable => maps:get(payable, FCode),
warnings => Warnings warnings => Warnings
}, },
ResDbg = Res#{variables_registers => VarsRegs}, {ok, maybe_generate_aci(Res, FoldedTypedAst, Options)}.
FinalRes =
case proplists:get_value(debug_info, Options, false) of
true -> ResDbg;
false -> Res
end,
{ok, maybe_generate_aci(FinalRes, FoldedTypedAst, Options)}.
maybe_generate_aci(Result, FoldedTypedAst, Options) -> maybe_generate_aci(Result, FoldedTypedAst, Options) ->
case proplists:get_value(aci, Options) of case proplists:get_value(aci, Options) of
@ -189,30 +188,58 @@ check_call(Source, FunName, Args, Options) ->
check_call1(Source, FunName, Args, Options). check_call1(Source, FunName, Args, Options).
check_call1(ContractString0, FunName, Args, Options) -> check_call1(ContractString0, FunName, Args, Options) ->
case add_extra_call(ContractString0, {call, FunName, Args}, Options) of
{ok, CallName, Code} ->
{def, _, _, FcodeArgs} = get_call_body(CallName, Code),
{ok, FunName, [ aeso_fcode_to_fate:term_to_fate(A) || A <- FcodeArgs ]};
Err = {error, _} ->
Err
end.
add_extra_call(Contract0, Call, Options) ->
try try
%% First check the contract without the __call function %% First check the contract without the __call function
#{fcode := OrgFcode #{fcode := OrgFcode
, fcode_env := #{child_con_env := ChildContracts} , fcode_env := #{child_con_env := ChildContracts}
, ast := Ast} = string_to_code(ContractString0, Options), , ast := Ast} = string_to_code(Contract0, Options),
{FateCode, _} = aeso_fcode_to_fate:compile(ChildContracts, OrgFcode, #{}, []), FateCode = aeso_fcode_to_fate:compile(ChildContracts, OrgFcode, #{}, []),
%% collect all hashes and compute the first name without hash collision to %% collect all hashes and compute the first name without hash collision to
SymbolHashes = maps:keys(aeb_fate_code:symbols(FateCode)), SymbolHashes = maps:keys(aeb_fate_code:symbols(FateCode)),
CallName = first_none_match(?CALL_NAME, SymbolHashes, CallName = first_none_match(?CALL_NAME, SymbolHashes,
lists:seq($1, $9) ++ lists:seq($A, $Z) ++ lists:seq($a, $z)), lists:seq($1, $9) ++ lists:seq($A, $Z) ++ lists:seq($a, $z)),
ContractString = insert_call_function(Ast, ContractString0, CallName, FunName, Args), Contract = insert_call_function(Ast, Contract0, CallName, Call),
#{fcode := Fcode} = string_to_code(ContractString, Options), {ok, CallName, string_to_code(Contract, Options)}
CallArgs = arguments_of_body(CallName, FunName, Fcode),
{ok, FunName, CallArgs}
catch catch
throw:{error, Errors} -> {error, Errors} throw:{error, Errors} -> {error, Errors}
end. end.
arguments_of_body(CallName, _FunName, Fcode) -> get_call_body(CallName, #{fcode := Fcode}) ->
#{body := Body} = maps:get({entrypoint, list_to_binary(CallName)}, maps:get(functions, Fcode)), #{body := Body} = maps:get({entrypoint, list_to_binary(CallName)}, maps:get(functions, Fcode)),
{def, _FName, Args} = Body, Body.
%% FName is either {entrypoint, list_to_binary(FunName)} or 'init'
[ aeso_fcode_to_fate:term_to_fate(A) || A <- Args ]. encode_value(Contract0, Type, Value, Options) ->
case add_extra_call(Contract0, {value, Type, Value}, Options) of
{ok, CallName, Code} ->
Body = get_call_body(CallName, Code),
{ok, aeb_fate_encoding:serialize(aeso_fcode_to_fate:term_to_fate(Body))};
Err = {error, _} ->
Err
end.
decode_value(Contract0, Type, FateValue, Options) ->
case add_extra_call(Contract0, {type, Type}, Options) of
{ok, CallName, Code} ->
#{ folded_typed_ast := TypedAst
, type_env := TypeEnv} = Code,
{ok, _, Type0} = get_decode_type(CallName, TypedAst),
Type1 = aeso_ast_infer_types:unfold_types_in_type(TypeEnv, Type0,
[ unfold_record_types
, unfold_variant_types
, not_unfold_system_alias_types ]),
fate_data_to_sophia_value(Type0, Type1, FateValue);
Err = {error, _} ->
Err
end.
first_none_match(_CallName, _Hashes, []) -> first_none_match(_CallName, _Hashes, []) ->
error(unable_to_find_unique_call_name); error(unable_to_find_unique_call_name);
@ -225,14 +252,31 @@ first_none_match(CallName, Hashes, [Char|Chars]) ->
end. end.
%% Add the __call function to a contract. %% Add the __call function to a contract.
-spec insert_call_function(aeso_syntax:ast(), string(), string(), string(), [string()]) -> string(). -spec insert_call_function(aeso_syntax:ast(), string(), string(),
insert_call_function(Ast, Code, Call, FunName, Args) -> {call, string(), [string()]} | {value, string(), string()} | {type, string()}) -> string().
insert_call_function(Ast, Code, Call, {call, FunName, Args}) ->
Ind = last_contract_indent(Ast), Ind = last_contract_indent(Ast),
lists:flatten( lists:flatten(
[ Code, [ Code,
"\n\n", "\n\n",
lists:duplicate(Ind, " "), lists:duplicate(Ind, " "),
"stateful entrypoint ", Call, "() = ", FunName, "(", string:join(Args, ","), ")\n" "stateful entrypoint ", Call, "() = ", FunName, "(", string:join(Args, ","), ")\n"
]);
insert_call_function(Ast, Code, Call, {value, Type, Value}) ->
Ind = last_contract_indent(Ast),
lists:flatten(
[ Code,
"\n\n",
lists:duplicate(Ind, " "),
"entrypoint ", Call, "() : ", Type, " = ", Value, "\n"
]);
insert_call_function(Ast, Code, Call, {type, Type}) ->
Ind = last_contract_indent(Ast),
lists:flatten(
[ Code,
"\n\n",
lists:duplicate(Ind, " "),
"entrypoint ", Call, "(val : ", Type, ") : ", Type, " = val\n"
]). ]).
-spec insert_init_function(string(), options()) -> string(). -spec insert_init_function(string(), options()) -> string().
@ -271,26 +315,32 @@ to_sophia_value(ContractString, FunName, ok, Data, Options0) ->
Options = [no_code | Options0], Options = [no_code | Options0],
try try
Code = string_to_code(ContractString, Options), Code = string_to_code(ContractString, Options),
#{ unfolded_typed_ast := TypedAst, type_env := TypeEnv} = Code, #{ folded_typed_ast := TypedAst, type_env := TypeEnv} = Code,
{ok, _, Type0} = get_decode_type(FunName, TypedAst), {ok, _, Type0} = get_decode_type(FunName, TypedAst),
Type = aeso_ast_infer_types:unfold_types_in_type(TypeEnv, Type0, [unfold_record_types, unfold_variant_types]), Type = aeso_ast_infer_types:unfold_types_in_type(TypeEnv, Type0,
[ unfold_record_types
, unfold_variant_types
, not_unfold_system_alias_types]),
try fate_data_to_sophia_value(Type0, Type, Data)
{ok, aeso_vm_decode:from_fate(Type, aeb_fate_encoding:deserialize(Data))}
catch throw:cannot_translate_to_sophia ->
Type1 = prettypr:format(aeso_pretty:type(Type0)),
Msg = io_lib:format("Cannot translate FATE value ~p\n of Sophia type ~s",
[aeb_fate_encoding:deserialize(Data), Type1]),
{error, [aeso_errors:new(data_error, Msg)]};
_:_ ->
Type1 = prettypr:format(aeso_pretty:type(Type0)),
Msg = io_lib:format("Failed to decode binary as type ~s", [Type1]),
{error, [aeso_errors:new(data_error, Msg)]}
end
catch catch
throw:{error, Errors} -> {error, Errors} throw:{error, Errors} -> {error, Errors}
end. end.
fate_data_to_sophia_value(Type, UnfoldedType, FateData) ->
try
{ok, aeso_vm_decode:from_fate(UnfoldedType, aeb_fate_encoding:deserialize(FateData))}
catch throw:cannot_translate_to_sophia ->
Type1 = prettypr:format(aeso_pretty:type(Type)),
Msg = io_lib:format("Cannot translate FATE value ~p\n of Sophia type ~s",
[aeb_fate_encoding:deserialize(FateData), Type1]),
{error, [aeso_errors:new(data_error, Msg)]};
_:_ ->
Type1 = prettypr:format(aeso_pretty:type(Type)),
Msg = io_lib:format("Failed to decode binary as type ~s", [Type1]),
{error, [aeso_errors:new(data_error, Msg)]}
end.
-spec create_calldata(string(), string(), [string()]) -> -spec create_calldata(string(), string(), [string()]) ->
{ok, binary()} | {error, [aeso_errors:error()]}. {ok, binary()} | {error, [aeso_errors:error()]}.
create_calldata(Code, Fun, Args) -> create_calldata(Code, Fun, Args) ->
@ -317,14 +367,17 @@ decode_calldata(ContractString, FunName, Calldata, Options0) ->
Options = [no_code | Options0], Options = [no_code | Options0],
try try
Code = string_to_code(ContractString, Options), Code = string_to_code(ContractString, Options),
#{ unfolded_typed_ast := TypedAst, type_env := TypeEnv} = Code, #{ folded_typed_ast := TypedAst, type_env := TypeEnv} = Code,
{ok, Args, _} = get_decode_type(FunName, TypedAst), {ok, Args, _} = get_decode_type(FunName, TypedAst),
GetType = fun({typed, _, _, T}) -> T; (T) -> T end, GetType = fun({typed, _, _, T}) -> T; (T) -> T end,
ArgTypes = lists:map(GetType, Args), ArgTypes = lists:map(GetType, Args),
Type0 = {tuple_t, [], ArgTypes}, Type0 = {tuple_t, [], ArgTypes},
%% user defined data types such as variants needed to match against %% user defined data types such as variants needed to match against
Type = aeso_ast_infer_types:unfold_types_in_type(TypeEnv, Type0, [unfold_record_types, unfold_variant_types]), Type = aeso_ast_infer_types:unfold_types_in_type(TypeEnv, Type0,
[ unfold_record_types
, unfold_variant_types
, not_unfold_system_alias_types]),
case aeb_fate_abi:decode_calldata(FunName, Calldata) of case aeb_fate_abi:decode_calldata(FunName, Calldata) of
{ok, FateArgs} -> {ok, FateArgs} ->
try try

View File

@ -53,7 +53,8 @@
tailpos = true, tailpos = true,
child_contracts = #{}, child_contracts = #{},
saved_fresh_names = #{}, saved_fresh_names = #{},
options = [] }). options = [],
debug_info = false }).
%% -- Debugging -------------------------------------------------------------- %% -- Debugging --------------------------------------------------------------
@ -82,24 +83,16 @@ code_error(Err) ->
compile(FCode, SavedFreshNames, Options) -> compile(FCode, SavedFreshNames, Options) ->
compile(#{}, FCode, SavedFreshNames, Options). compile(#{}, FCode, SavedFreshNames, Options).
compile(ChildContracts, FCode, SavedFreshNames, Options) -> compile(ChildContracts, FCode, SavedFreshNames, Options) ->
try
compile1(ChildContracts, FCode, SavedFreshNames, Options)
after
put(variables_registers, undefined)
end.
compile1(ChildContracts, FCode, SavedFreshNames, Options) ->
#{ contract_name := ContractName, #{ contract_name := ContractName,
functions := Functions } = FCode, functions := Functions } = FCode,
SFuns = functions_to_scode(ChildContracts, ContractName, Functions, SavedFreshNames, Options), SFuns = functions_to_scode(ChildContracts, ContractName, Functions, SavedFreshNames, Options),
SFuns1 = optimize_scode(SFuns, Options), SFuns1 = optimize_scode(SFuns, Options),
FateCode = to_basic_blocks(SFuns1), FateCode = to_basic_blocks(SFuns1),
?debug(compile, Options, "~s\n", [aeb_fate_asm:pp(FateCode)]), ?debug(compile, Options, "~s\n", [aeb_fate_asm:pp(FateCode)]),
FateCode1 = case proplists:get_value(include_child_contract_symbols, Options, false) of case proplists:get_value(include_child_contract_symbols, Options, false) of
false -> FateCode; false -> FateCode;
true -> add_child_symbols(ChildContracts, FateCode) true -> add_child_symbols(ChildContracts, FateCode)
end, end.
{FateCode1, get_variables_registers()}.
make_function_id(X) -> make_function_id(X) ->
aeb_fate_code:symbol_identifier(make_function_name(X)). aeb_fate_code:symbol_identifier(make_function_name(X)).
@ -124,31 +117,15 @@ functions_to_scode(ChildContracts, ContractName, Functions, SavedFreshNames, Opt
function_to_scode(ChildContracts, ContractName, Functions, Name, Attrs0, Args, Body, ResType, SavedFreshNames, Options) -> function_to_scode(ChildContracts, ContractName, Functions, Name, Attrs0, Args, Body, ResType, SavedFreshNames, Options) ->
{ArgTypes, ResType1} = typesig_to_scode(Args, ResType), {ArgTypes, ResType1} = typesig_to_scode(Args, ResType),
Attrs = Attrs0 -- [stateful], %% Only track private and payable from here. Attrs = [ A || A <- Attrs0, A == private orelse A == payable ],
Env = init_env(ChildContracts, ContractName, Functions, Name, Args, SavedFreshNames, Options), Env = init_env(ChildContracts, ContractName, Functions, Name, Args, SavedFreshNames, Options),
[ add_variables_register(Env, Arg, Register) || ArgsNames = [ X || {X, _} <- lists:reverse(Env#env.vars) ],
proplists:get_value(debug_info, Options, false),
{Arg, Register} <- Env#env.vars ], %% DBG_LOC is added before the function body to make it possible to break
%% at the function signature
SCode = to_scode(Env, Body), SCode = to_scode(Env, Body),
{Attrs, {ArgTypes, ResType1}, SCode}. DbgSCode = dbg_contract(Env) ++ dbg_loc(Env, Attrs0) ++ dbg_scoped_vars(Env, ArgsNames, SCode),
{Attrs, {ArgTypes, ResType1}, DbgSCode}.
get_variables_registers() ->
case get(variables_registers) of
undefined -> #{};
Vs -> Vs
end.
add_variables_register(Env = #env{saved_fresh_names = SavedFreshNames}, Name, Register) ->
Olds = get_variables_registers(),
RealName = maps:get(Name, SavedFreshNames, Name),
FunName =
case Env#env.current_function of
event -> "Chain.event";
{entrypoint, BinName} -> binary_to_list(BinName);
{local_fun, QualName} -> lists:last(QualName)
end,
New = {Env#env.contract, FunName, RealName},
put(variables_registers, Olds#{New => Register}).
-define(tvars, '$tvars'). -define(tvars, '$tvars').
@ -195,20 +172,20 @@ types_to_scode(Ts) -> lists:map(fun type_to_scode/1, Ts).
%% -- Environment functions -- %% -- Environment functions --
init_env(ChildContracts, ContractName, FunNames, Name, Args, SavedFreshNames, Options) -> init_env(ChildContracts, ContractName, FunNames, Name, Args, SavedFreshNames, Options) ->
#env{ vars = [ {X, {arg, I}} || {I, {X, _}} <- with_ixs(Args) ], #env{ vars = [ {X, {arg, I}} || {I, {X, _}} <- with_ixs(Args) ],
contract = ContractName, contract = ContractName,
child_contracts = ChildContracts, child_contracts = ChildContracts,
locals = FunNames, locals = FunNames,
current_function = Name, current_function = Name,
options = Options, options = Options,
tailpos = true, tailpos = true,
saved_fresh_names = SavedFreshNames }. saved_fresh_names = SavedFreshNames,
debug_info = proplists:get_value(debug_info, Options, false) }.
next_var(#env{ vars = Vars }) -> next_var(#env{ vars = Vars }) ->
1 + lists:max([-1 | [J || {_, {var, J}} <- Vars]]). 1 + lists:max([-1 | [J || {_, {var, J}} <- Vars]]).
bind_var(Name, Var, Env = #env{ vars = Vars }) -> bind_var(Name, Var, Env = #env{ vars = Vars }) ->
proplists:get_value(debug_info, Env#env.options, false) andalso add_variables_register(Env, Name, Var),
Env#env{ vars = [{Name, Var} | Vars] }. Env#env{ vars = [{Name, Var} | Vars] }.
bind_local(Name, Env) -> bind_local(Name, Env) ->
@ -235,7 +212,7 @@ serialize_contract_code(Env, C) ->
Options = Env#env.options, Options = Env#env.options,
SavedFreshNames = Env#env.saved_fresh_names, SavedFreshNames = Env#env.saved_fresh_names,
FCode = maps:get(C, Env#env.child_contracts), FCode = maps:get(C, Env#env.child_contracts),
{FateCode, _} = compile1(Env#env.child_contracts, FCode, SavedFreshNames, Options), FateCode = compile(Env#env.child_contracts, FCode, SavedFreshNames, Options),
ByteCode = aeb_fate_code:serialize(FateCode, []), ByteCode = aeb_fate_code:serialize(FateCode, []),
{ok, Version} = aeso_compiler:version(), {ok, Version} = aeso_compiler:version(),
OriginalSourceCode = proplists:get_value(original_src, Options, ""), OriginalSourceCode = proplists:get_value(original_src, Options, ""),
@ -259,6 +236,7 @@ lit_to_fate(Env, L) ->
{bytes, B} -> aeb_fate_data:make_bytes(B); {bytes, B} -> aeb_fate_data:make_bytes(B);
{bool, B} -> aeb_fate_data:make_boolean(B); {bool, B} -> aeb_fate_data:make_boolean(B);
{account_pubkey, K} -> aeb_fate_data:make_address(K); {account_pubkey, K} -> aeb_fate_data:make_address(K);
{signature, S} -> aeb_fate_data:make_bytes(S);
{contract_pubkey, K} -> aeb_fate_data:make_contract(K); {contract_pubkey, K} -> aeb_fate_data:make_contract(K);
{oracle_pubkey, K} -> aeb_fate_data:make_oracle(K); {oracle_pubkey, K} -> aeb_fate_data:make_oracle(K);
{oracle_query_id, H} -> aeb_fate_data:make_oracle_query(H); {oracle_query_id, H} -> aeb_fate_data:make_oracle_query(H);
@ -269,97 +247,106 @@ lit_to_fate(Env, L) ->
term_to_fate(E) -> term_to_fate(#env{}, #{}, E). term_to_fate(E) -> term_to_fate(#env{}, #{}, E).
term_to_fate(GlobEnv, E) -> term_to_fate(GlobEnv, #{}, E). term_to_fate(GlobEnv, E) -> term_to_fate(GlobEnv, #{}, E).
term_to_fate(GlobEnv, _Env, {lit, L}) -> term_to_fate(GlobEnv, _Env, {lit, _, L}) ->
lit_to_fate(GlobEnv, L); lit_to_fate(GlobEnv, L);
%% negative literals are parsed as 0 - N %% negative literals are parsed as 0 - N
term_to_fate(_GlobEnv, _Env, {op, '-', [{lit, {int, 0}}, {lit, {int, N}}]}) -> term_to_fate(_GlobEnv, _Env, {op, _, '-', [{lit, _, {int, 0}}, {lit, _, {int, N}}]}) ->
aeb_fate_data:make_integer(-N); aeb_fate_data:make_integer(-N);
term_to_fate(_GlobEnv, _Env, nil) -> term_to_fate(_GlobEnv, _Env, {nil, _}) ->
aeb_fate_data:make_list([]); aeb_fate_data:make_list([]);
term_to_fate(GlobEnv, Env, {op, '::', [Hd, Tl]}) -> term_to_fate(GlobEnv, Env, {op, _, '::', [Hd, Tl]}) ->
%% The Tl will translate into a list, because FATE lists are just lists %% The Tl will translate into a list, because FATE lists are just lists
[term_to_fate(GlobEnv, Env, Hd) | term_to_fate(GlobEnv, Env, Tl)]; [term_to_fate(GlobEnv, Env, Hd) | term_to_fate(GlobEnv, Env, Tl)];
term_to_fate(GlobEnv, Env, {tuple, As}) -> term_to_fate(GlobEnv, Env, {tuple, _, As}) ->
aeb_fate_data:make_tuple(list_to_tuple([ term_to_fate(GlobEnv, Env, A) || A<-As])); aeb_fate_data:make_tuple(list_to_tuple([ term_to_fate(GlobEnv, Env, A) || A<-As]));
term_to_fate(GlobEnv, Env, {con, Ar, I, As}) -> term_to_fate(GlobEnv, Env, {con, _, Ar, I, As}) ->
FateAs = [ term_to_fate(GlobEnv, Env, A) || A <- As ], FateAs = [ term_to_fate(GlobEnv, Env, A) || A <- As ],
aeb_fate_data:make_variant(Ar, I, list_to_tuple(FateAs)); aeb_fate_data:make_variant(Ar, I, list_to_tuple(FateAs));
term_to_fate(_GlobEnv, _Env, {builtin, bits_all, []}) -> term_to_fate(_GlobEnv, _Env, {builtin, _, bits_all, []}) ->
aeb_fate_data:make_bits(-1); aeb_fate_data:make_bits(-1);
term_to_fate(_GlobEnv, _Env, {builtin, bits_none, []}) -> term_to_fate(_GlobEnv, _Env, {builtin, _, bits_none, []}) ->
aeb_fate_data:make_bits(0); aeb_fate_data:make_bits(0);
term_to_fate(GlobEnv, _Env, {op, bits_set, [B, I]}) -> term_to_fate(GlobEnv, _Env, {op, _, bits_set, [B, I]}) ->
{bits, N} = term_to_fate(GlobEnv, B), {bits, N} = term_to_fate(GlobEnv, B),
J = term_to_fate(GlobEnv, I), J = term_to_fate(GlobEnv, I),
{bits, N bor (1 bsl J)}; {bits, N bor (1 bsl J)};
term_to_fate(GlobEnv, _Env, {op, bits_clear, [B, I]}) -> term_to_fate(GlobEnv, _Env, {op, _, bits_clear, [B, I]}) ->
{bits, N} = term_to_fate(GlobEnv, B), {bits, N} = term_to_fate(GlobEnv, B),
J = term_to_fate(GlobEnv, I), J = term_to_fate(GlobEnv, I),
{bits, N band bnot (1 bsl J)}; {bits, N band bnot (1 bsl J)};
term_to_fate(GlobEnv, Env, {'let', X, E, Body}) -> term_to_fate(GlobEnv, Env, {'let', _, X, E, Body}) ->
Env1 = Env#{ X => term_to_fate(GlobEnv, Env, E) }, Env1 = Env#{ X => term_to_fate(GlobEnv, Env, E) },
term_to_fate(GlobEnv, Env1, Body); term_to_fate(GlobEnv, Env1, Body);
term_to_fate(_GlobEnv, Env, {var, X}) -> term_to_fate(_GlobEnv, Env, {var, _, X}) ->
case maps:get(X, Env, undefined) of case maps:get(X, Env, undefined) of
undefined -> throw(not_a_fate_value); undefined -> throw(not_a_fate_value);
V -> V V -> V
end; end;
term_to_fate(_GlobEnv, _Env, {builtin, map_empty, []}) -> term_to_fate(_GlobEnv, _Env, {builtin, _, map_empty, []}) ->
aeb_fate_data:make_map(#{}); aeb_fate_data:make_map(#{});
term_to_fate(GlobEnv, Env, {op, map_set, [M, K, V]}) -> term_to_fate(GlobEnv, Env, {op, _, map_set, [M, K, V]}) ->
Map = term_to_fate(GlobEnv, Env, M), Map = term_to_fate(GlobEnv, Env, M),
Map#{term_to_fate(GlobEnv, Env, K) => term_to_fate(GlobEnv, Env, V)}; Map#{term_to_fate(GlobEnv, Env, K) => term_to_fate(GlobEnv, Env, V)};
term_to_fate(GlobEnv, Env, {builtin, _, bytes_to_any_size, [Bs]}) ->
term_to_fate(GlobEnv, Env, Bs);
term_to_fate(_GlobEnv, _Env, _) -> term_to_fate(_GlobEnv, _Env, _) ->
throw(not_a_fate_value). throw(not_a_fate_value).
to_scode(Env, T) -> to_scode(Env, T) ->
try term_to_fate(Env, T) of try term_to_fate(Env, T) of
V -> [push(?i(V))] V ->
FAnn = element(2, T),
[dbg_loc(Env, FAnn), push(?i(V))]
catch throw:not_a_fate_value -> catch throw:not_a_fate_value ->
to_scode1(Env, T) to_scode1(Env, T)
end. end.
to_scode1(Env, {lit, L}) -> to_scode1(Env, {lit, Ann, L}) ->
[push(?i(lit_to_fate(Env, L)))]; [ dbg_loc(Env, Ann), push(?i(lit_to_fate(Env, L))) ];
to_scode1(_Env, nil) -> to_scode1(Env, {nil, Ann}) ->
[aeb_fate_ops:nil(?a)]; [ dbg_loc(Env, Ann), aeb_fate_ops:nil(?a) ];
to_scode1(Env, {var, X}) -> to_scode1(Env, {var, Ann, X}) ->
[push(lookup_var(Env, X))]; [ dbg_loc(Env, Ann), push(lookup_var(Env, X)) ];
to_scode1(Env, {con, Ar, I, As}) -> to_scode1(Env, {con, Ann, Ar, I, As}) ->
N = length(As), N = length(As),
[[to_scode(notail(Env), A) || A <- As], [ dbg_loc(Env, Ann),
aeb_fate_ops:variant(?a, ?i(Ar), ?i(I), ?i(N))]; [to_scode(notail(Env), A) || A <- As],
aeb_fate_ops:variant(?a, ?i(Ar), ?i(I), ?i(N)) ];
to_scode1(Env, {tuple, As}) -> to_scode1(Env, {tuple, Ann, As}) ->
N = length(As), N = length(As),
[[ to_scode(notail(Env), A) || A <- As ], [ dbg_loc(Env, Ann),
tuple(N)]; [ to_scode(notail(Env), A) || A <- As ],
tuple(N) ];
to_scode1(Env, {proj, E, I}) -> to_scode1(Env, {proj, Ann, E, I}) ->
[to_scode(notail(Env), E), [ dbg_loc(Env, Ann),
aeb_fate_ops:element_op(?a, ?i(I), ?a)]; to_scode(notail(Env), E),
aeb_fate_ops:element_op(?a, ?i(I), ?a) ];
to_scode1(Env, {set_proj, R, I, E}) -> to_scode1(Env, {set_proj, Ann, R, I, E}) ->
[to_scode(notail(Env), E), [ dbg_loc(Env, Ann),
to_scode(notail(Env), R), to_scode(notail(Env), E),
aeb_fate_ops:setelement(?a, ?i(I), ?a, ?a)]; to_scode(notail(Env), R),
aeb_fate_ops:setelement(?a, ?i(I), ?a, ?a) ];
to_scode1(Env, {op, Op, Args}) -> to_scode1(Env, {op, Ann, Op, Args}) ->
call_to_scode(Env, op_to_scode(Op), Args); [ dbg_loc(Env, Ann) | call_to_scode(Env, op_to_scode(Op), Args) ];
to_scode1(Env, {'let', X, {var, Y}, Body}) -> to_scode1(Env, {'let', Ann, X, {var, _, Y}, Body}) ->
Env1 = bind_var(X, lookup_var(Env, Y), Env), Env1 = bind_var(X, lookup_var(Env, Y), Env),
to_scode(Env1, Body); [ dbg_loc(Env, Ann) | dbg_scoped_vars(Env1, [X], to_scode(Env1, Body)) ];
to_scode1(Env, {'let', X, Expr, Body}) -> to_scode1(Env, {'let', Ann, X, Expr, Body}) ->
{I, Env1} = bind_local(X, Env), {I, Env1} = bind_local(X, Env),
[ to_scode(notail(Env), Expr), SCode = [ to_scode(notail(Env), Expr),
aeb_fate_ops:store({var, I}, {stack, 0}), aeb_fate_ops:store({var, I}, {stack, 0}),
to_scode(Env1, Body) ]; to_scode(Env1, Body) ],
[ dbg_loc(Env, Ann) | dbg_scoped_vars(Env1, [X], SCode) ];
to_scode1(Env = #env{ current_function = Fun, tailpos = true }, {def, Fun, Args}) -> to_scode1(Env = #env{ current_function = Fun, tailpos = true, debug_info = false }, {def, Ann, Fun, Args}) ->
%% Tail-call to current function, f(e0..en). Compile to %% Tail-call to current function, f(e0..en). Compile to
%% [ let xi = ei ] %% [ let xi = ei ]
%% [ STORE argi xi ] %% [ STORE argi xi ]
@ -372,61 +359,62 @@ to_scode1(Env = #env{ current_function = Fun, tailpos = true }, {def, Fun, Args}
aeb_fate_ops:store({var, I}, ?a)], aeb_fate_ops:store({var, I}, ?a)],
{[I | Is], Acc1, Env2} {[I | Is], Acc1, Env2}
end, {[], [], Env}, Args), end, {[], [], Env}, Args),
[ Code, [ dbg_loc(Env, Ann),
Code,
[ aeb_fate_ops:store({arg, I}, {var, J}) [ aeb_fate_ops:store({arg, I}, {var, J})
|| {I, J} <- lists:zip(lists:seq(0, length(Vars) - 1), || {I, J} <- lists:zip(lists:seq(0, length(Vars) - 1),
lists:reverse(Vars)) ], lists:reverse(Vars)) ],
loop ]; loop ];
to_scode1(Env, {def, Fun, Args}) -> to_scode1(Env, {def, Ann, Fun, Args}) ->
FName = make_function_id(Fun), FName = make_function_id(Fun),
Lbl = aeb_fate_data:make_string(FName), Lbl = aeb_fate_data:make_string(FName),
call_to_scode(Env, local_call(Env, ?i(Lbl)), Args); [ dbg_loc(Env, Ann) | call_to_scode(Env, local_call(Env, ?i(Lbl)), Args) ];
to_scode1(Env, {funcall, Fun, Args}) -> to_scode1(Env, {funcall, Ann, Fun, Args}) ->
call_to_scode(Env, [to_scode(Env, Fun), local_call(Env, ?a)], Args); [ dbg_loc(Env, Ann) | call_to_scode(Env, [to_scode(Env, Fun), local_call(Env, ?a)], Args) ];
to_scode1(Env, {builtin, B, Args}) -> to_scode1(Env, {builtin, Ann, B, Args}) ->
builtin_to_scode(Env, B, Args); [ dbg_loc(Env, Ann) | builtin_to_scode(Env, B, Args) ];
to_scode1(Env, {remote, ArgsT, RetT, Ct, Fun, [Gas, Value, Protected | Args]}) -> to_scode1(Env, {remote, Ann, ArgsT, RetT, Ct, Fun, [Gas, Value, Protected | Args]}) ->
Lbl = make_function_id(Fun), Lbl = make_function_id(Fun),
{ArgTypes, RetType0} = typesig_to_scode([{"_", T} || T <- ArgsT], RetT), {ArgTypes, RetType0} = typesig_to_scode([{"_", T} || T <- ArgsT], RetT),
ArgType = ?i(aeb_fate_data:make_typerep({tuple, ArgTypes})), ArgType = ?i(aeb_fate_data:make_typerep({tuple, ArgTypes})),
RetType = ?i(aeb_fate_data:make_typerep(RetType0)), RetType = ?i(aeb_fate_data:make_typerep(RetType0)),
case Protected of SCode = case Protected of
{lit, {bool, false}} -> {lit, _, {bool, false}} ->
case Gas of case Gas of
{builtin, call_gas_left, _} -> {builtin, _, call_gas_left, _} ->
Call = aeb_fate_ops:call_r(?a, Lbl, ArgType, RetType, ?a), Call = aeb_fate_ops:call_r(?a, Lbl, ArgType, RetType, ?a),
call_to_scode(Env, Call, [Ct, Value | Args]); call_to_scode(Env, Call, [Ct, Value | Args]);
_ -> _ ->
Call = aeb_fate_ops:call_gr(?a, Lbl, ArgType, RetType, ?a, ?a), Call = aeb_fate_ops:call_gr(?a, Lbl, ArgType, RetType, ?a, ?a),
call_to_scode(Env, Call, [Ct, Value, Gas | Args]) call_to_scode(Env, Call, [Ct, Value, Gas | Args])
end; end;
{lit, {bool, true}} -> {lit, _, {bool, true}} ->
Call = aeb_fate_ops:call_pgr(?a, Lbl, ArgType, RetType, ?a, ?a, ?i(true)), Call = aeb_fate_ops:call_pgr(?a, Lbl, ArgType, RetType, ?a, ?a, ?i(true)),
call_to_scode(Env, Call, [Ct, Value, Gas | Args]); call_to_scode(Env, Call, [Ct, Value, Gas | Args]);
_ -> _ ->
Call = aeb_fate_ops:call_pgr(?a, Lbl, ArgType, RetType, ?a, ?a, ?a), Call = aeb_fate_ops:call_pgr(?a, Lbl, ArgType, RetType, ?a, ?a, ?a),
call_to_scode(Env, Call, [Ct, Value, Gas, Protected | Args]) call_to_scode(Env, Call, [Ct, Value, Gas, Protected | Args])
end; end,
[ dbg_loc(Env, Ann) | SCode ];
to_scode1(_Env, {get_state, Reg}) -> to_scode1(Env, {get_state, Ann, Reg}) ->
[push(?s(Reg))]; [ dbg_loc(Env, Ann), push(?s(Reg)) ];
to_scode1(Env, {set_state, Reg, Val}) -> to_scode1(Env, {set_state, Ann, Reg, Val}) ->
call_to_scode(Env, [{'STORE', ?s(Reg), ?a}, [ dbg_loc(Env, Ann) | call_to_scode(Env, [{'STORE', ?s(Reg), ?a}, tuple(0)], [Val]) ];
tuple(0)], [Val]);
to_scode1(Env, {closure, Fun, FVs}) -> to_scode1(Env, {closure, Ann, Fun, FVs}) ->
to_scode(Env, {tuple, [{lit, {string, make_function_id(Fun)}}, FVs]}); [ to_scode(Env, {tuple, Ann, [{lit, Ann, {string, make_function_id(Fun)}}, FVs]}) ];
to_scode1(Env, {switch, Case}) -> to_scode1(Env, {switch, Ann, Case}) ->
split_to_scode(Env, Case). [ dbg_loc(Env, Ann) | split_to_scode(Env, Case) ].
local_call( Env, Fun) when Env#env.tailpos -> aeb_fate_ops:call_t(Fun); local_call( Env = #env{debug_info = false}, Fun) when Env#env.tailpos -> aeb_fate_ops:call_t(Fun);
local_call(_Env, Fun) -> aeb_fate_ops:call(Fun). local_call(_Env, Fun) -> aeb_fate_ops:call(Fun).
split_to_scode(Env, {nosplit, Expr}) -> split_to_scode(Env, {nosplit, Renames, Expr}) ->
[switch_body, to_scode(Env, Expr)]; [switch_body, dbg_scoped_vars(Env, Renames, to_scode(Env, Expr))];
split_to_scode(Env, {split, {tuple, _}, X, Alts}) -> split_to_scode(Env, {split, {tuple, _}, X, Alts}) ->
{Def, Alts1} = catchall_to_scode(Env, X, Alts), {Def, Alts1} = catchall_to_scode(Env, X, Alts),
Arg = lookup_var(Env, X), Arg = lookup_var(Env, X),
@ -555,6 +543,14 @@ builtin_to_scode(Env, bytes_concat, [_, _] = Args) ->
call_to_scode(Env, aeb_fate_ops:bytes_concat(?a, ?a, ?a), Args); call_to_scode(Env, aeb_fate_ops:bytes_concat(?a, ?a, ?a), Args);
builtin_to_scode(Env, bytes_split, [_, _] = Args) -> builtin_to_scode(Env, bytes_split, [_, _] = Args) ->
call_to_scode(Env, aeb_fate_ops:bytes_split(?a, ?a, ?a), Args); call_to_scode(Env, aeb_fate_ops:bytes_split(?a, ?a, ?a), Args);
builtin_to_scode(Env, bytes_split_any, [_, _] = Args) ->
call_to_scode(Env, aeb_fate_ops:bytes_split_any(?a, ?a, ?a), Args);
builtin_to_scode(Env, bytes_to_fixed_size, [_, _] = Args) ->
call_to_scode(Env, aeb_fate_ops:bytes_to_fixed_size(?a, ?a, ?a), Args);
builtin_to_scode(Env, bytes_to_any_size, [A]) ->
[to_scode(Env, A)]; %% no_op!
builtin_to_scode(Env, bytes_size, [_] = Args) ->
call_to_scode(Env, aeb_fate_ops:bytes_size(?a, ?a), Args);
builtin_to_scode(Env, abort, [_] = Args) -> builtin_to_scode(Env, abort, [_] = Args) ->
call_to_scode(Env, aeb_fate_ops:abort(?a), Args); call_to_scode(Env, aeb_fate_ops:abort(?a), Args);
builtin_to_scode(Env, exit, [_] = Args) -> builtin_to_scode(Env, exit, [_] = Args) ->
@ -576,6 +572,8 @@ builtin_to_scode(_Env, chain_difficulty, []) ->
[aeb_fate_ops:difficulty(?a)]; [aeb_fate_ops:difficulty(?a)];
builtin_to_scode(_Env, chain_gas_limit, []) -> builtin_to_scode(_Env, chain_gas_limit, []) ->
[aeb_fate_ops:gaslimit(?a)]; [aeb_fate_ops:gaslimit(?a)];
builtin_to_scode(_Env, chain_network_id, []) ->
[aeb_fate_ops:network_id(?a)];
builtin_to_scode(_Env, contract_balance, []) -> builtin_to_scode(_Env, contract_balance, []) ->
[aeb_fate_ops:balance(?a)]; [aeb_fate_ops:balance(?a)];
builtin_to_scode(_Env, contract_address, []) -> builtin_to_scode(_Env, contract_address, []) ->
@ -650,7 +648,7 @@ builtin_to_scode(Env, chain_bytecode_hash, [_Addr] = Args) ->
builtin_to_scode(Env, chain_clone, builtin_to_scode(Env, chain_clone,
[InitArgsT, GasCap, Value, Prot, Contract | InitArgs]) -> [InitArgsT, GasCap, Value, Prot, Contract | InitArgs]) ->
case GasCap of case GasCap of
{builtin, call_gas_left, _} -> {builtin, _, call_gas_left, _} ->
call_to_scode(Env, aeb_fate_ops:clone(?a, ?a, ?a, ?a), call_to_scode(Env, aeb_fate_ops:clone(?a, ?a, ?a, ?a),
[Contract, InitArgsT, Value, Prot | InitArgs] [Contract, InitArgsT, Value, Prot | InitArgs]
); );
@ -683,6 +681,12 @@ op_to_scode('>=') -> aeb_fate_ops:egt(?a, ?a, ?a);
op_to_scode('==') -> aeb_fate_ops:eq(?a, ?a, ?a); op_to_scode('==') -> aeb_fate_ops:eq(?a, ?a, ?a);
op_to_scode('!=') -> aeb_fate_ops:neq(?a, ?a, ?a); op_to_scode('!=') -> aeb_fate_ops:neq(?a, ?a, ?a);
op_to_scode('!') -> aeb_fate_ops:not_op(?a, ?a); op_to_scode('!') -> aeb_fate_ops:not_op(?a, ?a);
op_to_scode('bnot') -> aeb_fate_ops:bin_not(?a, ?a);
op_to_scode('band') -> aeb_fate_ops:bin_and(?a, ?a, ?a);
op_to_scode('bor') -> aeb_fate_ops:bin_or(?a, ?a, ?a);
op_to_scode('bxor') -> aeb_fate_ops:bin_xor(?a, ?a, ?a);
op_to_scode('<<') -> aeb_fate_ops:bin_sl(?a, ?a, ?a);
op_to_scode('>>') -> aeb_fate_ops:bin_sr(?a, ?a, ?a);
op_to_scode(map_get) -> aeb_fate_ops:map_lookup(?a, ?a, ?a); op_to_scode(map_get) -> aeb_fate_ops:map_lookup(?a, ?a, ?a);
op_to_scode(map_get_d) -> aeb_fate_ops:map_lookup(?a, ?a, ?a, ?a); op_to_scode(map_get_d) -> aeb_fate_ops:map_lookup(?a, ?a, ?a, ?a);
op_to_scode(map_set) -> aeb_fate_ops:map_update(?a, ?a, ?a, ?a); op_to_scode(map_set) -> aeb_fate_ops:map_update(?a, ?a, ?a, ?a);
@ -693,6 +697,7 @@ op_to_scode(map_member) -> aeb_fate_ops:map_member(?a, ?a, ?a);
op_to_scode(map_size) -> aeb_fate_ops:map_size_(?a, ?a); op_to_scode(map_size) -> aeb_fate_ops:map_size_(?a, ?a);
op_to_scode(stringinternal_length) -> aeb_fate_ops:str_length(?a, ?a); op_to_scode(stringinternal_length) -> aeb_fate_ops:str_length(?a, ?a);
op_to_scode(stringinternal_concat) -> aeb_fate_ops:str_join(?a, ?a, ?a); op_to_scode(stringinternal_concat) -> aeb_fate_ops:str_join(?a, ?a, ?a);
op_to_scode(stringinternal_to_bytes) -> aeb_fate_ops:str_to_bytes(?a, ?a);
op_to_scode(stringinternal_to_list) -> aeb_fate_ops:str_to_list(?a, ?a); op_to_scode(stringinternal_to_list) -> aeb_fate_ops:str_to_list(?a, ?a);
op_to_scode(stringinternal_from_list) -> aeb_fate_ops:str_from_list(?a, ?a); op_to_scode(stringinternal_from_list) -> aeb_fate_ops:str_from_list(?a, ?a);
op_to_scode(stringinternal_to_lower) -> aeb_fate_ops:str_to_lower(?a, ?a); op_to_scode(stringinternal_to_lower) -> aeb_fate_ops:str_to_lower(?a, ?a);
@ -707,7 +712,10 @@ op_to_scode(bits_intersection) -> aeb_fate_ops:bits_and(?a, ?a, ?a);
op_to_scode(bits_union) -> aeb_fate_ops:bits_or(?a, ?a, ?a); op_to_scode(bits_union) -> aeb_fate_ops:bits_or(?a, ?a, ?a);
op_to_scode(bits_difference) -> aeb_fate_ops:bits_diff(?a, ?a, ?a); op_to_scode(bits_difference) -> aeb_fate_ops:bits_diff(?a, ?a, ?a);
op_to_scode(address_to_str) -> aeb_fate_ops:addr_to_str(?a, ?a); op_to_scode(address_to_str) -> aeb_fate_ops:addr_to_str(?a, ?a);
op_to_scode(address_to_bytes) -> aeb_fate_ops:addr_to_bytes(?a, ?a);
op_to_scode(int_to_str) -> aeb_fate_ops:int_to_str(?a, ?a); op_to_scode(int_to_str) -> aeb_fate_ops:int_to_str(?a, ?a);
op_to_scode(int_to_bytes) -> aeb_fate_ops:int_to_bytes(?a, ?a, ?a);
op_to_scode(int_mulmod) -> aeb_fate_ops:mulmod(?a, ?a, ?a, ?a);
op_to_scode(contract_to_address) -> aeb_fate_ops:contract_to_address(?a, ?a); op_to_scode(contract_to_address) -> aeb_fate_ops:contract_to_address(?a, ?a);
op_to_scode(address_to_contract) -> aeb_fate_ops:address_to_contract(?a, ?a); op_to_scode(address_to_contract) -> aeb_fate_ops:address_to_contract(?a, ?a);
op_to_scode(crypto_verify_sig) -> aeb_fate_ops:verify_sig(?a, ?a, ?a, ?a); op_to_scode(crypto_verify_sig) -> aeb_fate_ops:verify_sig(?a, ?a, ?a, ?a);
@ -717,6 +725,7 @@ op_to_scode(crypto_ecrecover_secp256k1) -> aeb_fate_ops:ecrecover_secp256k1(?a,
op_to_scode(crypto_sha3) -> aeb_fate_ops:sha3(?a, ?a); op_to_scode(crypto_sha3) -> aeb_fate_ops:sha3(?a, ?a);
op_to_scode(crypto_sha256) -> aeb_fate_ops:sha256(?a, ?a); op_to_scode(crypto_sha256) -> aeb_fate_ops:sha256(?a, ?a);
op_to_scode(crypto_blake2b) -> aeb_fate_ops:blake2b(?a, ?a); op_to_scode(crypto_blake2b) -> aeb_fate_ops:blake2b(?a, ?a);
op_to_scode(crypto_poseidon) -> aeb_fate_ops:poseidon(?a, ?a, ?a);
op_to_scode(stringinternal_sha3) -> aeb_fate_ops:sha3(?a, ?a); op_to_scode(stringinternal_sha3) -> aeb_fate_ops:sha3(?a, ?a);
op_to_scode(stringinternal_sha256) -> aeb_fate_ops:sha256(?a, ?a); op_to_scode(stringinternal_sha256) -> aeb_fate_ops:sha256(?a, ?a);
op_to_scode(stringinternal_blake2b) -> aeb_fate_ops:blake2b(?a, ?a); op_to_scode(stringinternal_blake2b) -> aeb_fate_ops:blake2b(?a, ?a);
@ -752,6 +761,77 @@ push(A) -> {'STORE', ?a, A}.
tuple(0) -> push(?i({tuple, {}})); tuple(0) -> push(?i({tuple, {}}));
tuple(N) -> aeb_fate_ops:tuple(?a, N). tuple(N) -> aeb_fate_ops:tuple(?a, N).
%% -- Debug info functions --
dbg_contract(#env{debug_info = false}) ->
[];
dbg_contract(#env{contract = Contract}) ->
[{'DBG_CONTRACT', {immediate, Contract}}].
dbg_loc(#env{debug_info = false}, _) ->
[];
dbg_loc(_Env, Ann) ->
File = case proplists:get_value(file, Ann, no_file) of
no_file -> "";
F -> F
end,
Line = proplists:get_value(line, Ann, undefined),
case Line of
undefined -> [];
_ -> [{'DBG_LOC', {immediate, File}, {immediate, Line}}]
end.
dbg_scoped_vars(#env{debug_info = false}, _, SCode) ->
SCode;
dbg_scoped_vars(_Env, [], SCode) ->
SCode;
dbg_scoped_vars(Env, [{SavedVarName, Var} | Rest], SCode) ->
dbg_scoped_vars(Env, Rest, dbg_scoped_var(Env, SavedVarName, Var, SCode));
dbg_scoped_vars(Env = #env{saved_fresh_names = SavedFreshNames}, [Var | Rest], SCode) ->
SavedVarName = maps:get(Var, SavedFreshNames, Var),
dbg_scoped_vars(Env, Rest, dbg_scoped_var(Env, SavedVarName, Var, SCode)).
dbg_scoped_var(Env, SavedVarName, Var, SCode) ->
case SavedVarName == "_" orelse is_fresh_name(SavedVarName) of
true ->
SCode;
false ->
Register = lookup_var(Env, Var),
Def = [{'DBG_DEF', {immediate, SavedVarName}, Register}],
Undef = [{'DBG_UNDEF', {immediate, SavedVarName}, Register}],
Def ++ dbg_undef(Undef, SCode)
end.
is_fresh_name([$% | _]) ->
true;
is_fresh_name(_) ->
false.
dbg_undef(_Undef, missing) ->
missing;
dbg_undef(Undef, loop) ->
[Undef, loop];
dbg_undef(Undef, switch_body) ->
[switch_body, Undef];
dbg_undef(Undef, {switch, Arg, Type, Alts, Catch}) ->
NewAlts = [ dbg_undef(Undef, Alt) || Alt <- Alts ],
NewCatch = dbg_undef(Undef, Catch),
NewSwitch = {switch, Arg, Type, NewAlts, NewCatch},
NewSwitch;
dbg_undef(Undef, SCode) when is_list(SCode) ->
lists:droplast(SCode) ++ [dbg_undef(Undef, lists:last(SCode))];
dbg_undef(Undef, SCode) when is_tuple(SCode); is_atom(SCode) ->
[Mnemonic | _] =
case is_tuple(SCode) of
true -> tuple_to_list(SCode);
false -> [SCode]
end,
Op = aeb_fate_opcodes:m_to_op(Mnemonic),
case aeb_fate_opcodes:end_bb(Op) of
true -> [Undef, SCode];
false -> [SCode, Undef]
end.
%% -- Phase II --------------------------------------------------------------- %% -- Phase II ---------------------------------------------------------------
%% Optimize %% Optimize
@ -887,6 +967,10 @@ attributes(I) ->
loop -> Impure(pc, []); loop -> Impure(pc, []);
switch_body -> Pure(none, []); switch_body -> Pure(none, []);
'RETURN' -> Impure(pc, []); 'RETURN' -> Impure(pc, []);
{'DBG_LOC', _, _} -> Impure(none, []);
{'DBG_DEF', _, _} -> Impure(none, []);
{'DBG_UNDEF', _, _} -> Impure(none, []);
{'DBG_CONTRACT', _} -> Impure(none, []);
{'RETURNR', A} -> Impure(pc, A); {'RETURNR', A} -> Impure(pc, A);
{'CALL', A} -> Impure(?a, [A]); {'CALL', A} -> Impure(?a, [A]);
{'CALL_R', A, _, B, C, D} -> Impure(?a, [A, B, C, D]); {'CALL_R', A, _, B, C, D} -> Impure(?a, [A, B, C, D]);
@ -914,6 +998,13 @@ attributes(I) ->
{'DIV', A, B, C} -> Pure(A, [B, C]); {'DIV', A, B, C} -> Pure(A, [B, C]);
{'MOD', A, B, C} -> Pure(A, [B, C]); {'MOD', A, B, C} -> Pure(A, [B, C]);
{'POW', A, B, C} -> Pure(A, [B, C]); {'POW', A, B, C} -> Pure(A, [B, C]);
{'MULMOD', A, B, C, D} -> Pure(A, [B, C, D]);
{'BAND', A, B, C} -> Pure(A, [B, C]);
{'BOR', A, B, C} -> Pure(A, [B, C]);
{'BXOR', A, B, C} -> Pure(A, [B, C]);
{'BNOT', A, B} -> Pure(A, [B]);
{'BSL', A, B, C} -> Pure(A, [B, C]);
{'BSR', A, B, C} -> Pure(A, [B, C]);
{'LT', A, B, C} -> Pure(A, [B, C]); {'LT', A, B, C} -> Pure(A, [B, C]);
{'GT', A, B, C} -> Pure(A, [B, C]); {'GT', A, B, C} -> Pure(A, [B, C]);
{'EQ', A, B, C} -> Pure(A, [B, C]); {'EQ', A, B, C} -> Pure(A, [B, C]);
@ -944,9 +1035,11 @@ attributes(I) ->
{'APPEND', A, B, C} -> Pure(A, [B, C]); {'APPEND', A, B, C} -> Pure(A, [B, C]);
{'STR_JOIN', A, B, C} -> Pure(A, [B, C]); {'STR_JOIN', A, B, C} -> Pure(A, [B, C]);
{'INT_TO_STR', A, B} -> Pure(A, B); {'INT_TO_STR', A, B} -> Pure(A, B);
{'INT_TO_BYTES', A, B, C} -> Pure(A, [B, C]);
{'ADDR_TO_STR', A, B} -> Pure(A, B); {'ADDR_TO_STR', A, B} -> Pure(A, B);
{'STR_REVERSE', A, B} -> Pure(A, B); {'STR_REVERSE', A, B} -> Pure(A, B);
{'STR_LENGTH', A, B} -> Pure(A, B); {'STR_LENGTH', A, B} -> Pure(A, B);
{'STR_TO_BYTES', A, B} -> Pure(A, B);
{'INT_TO_ADDR', A, B} -> Pure(A, B); {'INT_TO_ADDR', A, B} -> Pure(A, B);
{'VARIANT', A, B, C, D} -> Pure(A, [?a, B, C, D]); {'VARIANT', A, B, C, D} -> Pure(A, [?a, B, C, D]);
{'VARIANT_TEST', A, B, C} -> Pure(A, [B, C]); {'VARIANT_TEST', A, B, C} -> Pure(A, [B, C]);
@ -966,18 +1059,23 @@ attributes(I) ->
{'SHA3', A, B} -> Pure(A, [B]); {'SHA3', A, B} -> Pure(A, [B]);
{'SHA256', A, B} -> Pure(A, [B]); {'SHA256', A, B} -> Pure(A, [B]);
{'BLAKE2B', A, B} -> Pure(A, [B]); {'BLAKE2B', A, B} -> Pure(A, [B]);
{'POSEIDON', A, B, C} -> Pure(A, [B, C]);
{'VERIFY_SIG', A, B, C, D} -> Pure(A, [B, C, D]); {'VERIFY_SIG', A, B, C, D} -> Pure(A, [B, C, D]);
{'VERIFY_SIG_SECP256K1', A, B, C, D} -> Pure(A, [B, C, D]); {'VERIFY_SIG_SECP256K1', A, B, C, D} -> Pure(A, [B, C, D]);
{'ECVERIFY_SECP256K1', A, B, C, D} -> Pure(A, [B, C, D]); {'ECVERIFY_SECP256K1', A, B, C, D} -> Pure(A, [B, C, D]);
{'ECRECOVER_SECP256K1', A, B, C} -> Pure(A, [B, C]); {'ECRECOVER_SECP256K1', A, B, C} -> Pure(A, [B, C]);
{'CONTRACT_TO_ADDRESS', A, B} -> Pure(A, [B]); {'CONTRACT_TO_ADDRESS', A, B} -> Pure(A, [B]);
{'ADDRESS_TO_CONTRACT', A, B} -> Pure(A, [B]); {'ADDRESS_TO_CONTRACT', A, B} -> Pure(A, [B]);
{'ADDRESS_TO_BYTES', A, B} -> Pure(A, [B]);
{'AUTH_TX_HASH', A} -> Pure(A, []); {'AUTH_TX_HASH', A} -> Pure(A, []);
{'AUTH_TX', A} -> Pure(A, []); {'AUTH_TX', A} -> Pure(A, []);
{'BYTES_TO_INT', A, B} -> Pure(A, [B]); {'BYTES_TO_INT', A, B} -> Pure(A, [B]);
{'BYTES_TO_STR', A, B} -> Pure(A, [B]); {'BYTES_TO_STR', A, B} -> Pure(A, [B]);
{'BYTES_CONCAT', A, B, C} -> Pure(A, [B, C]); {'BYTES_CONCAT', A, B, C} -> Pure(A, [B, C]);
{'BYTES_SPLIT', A, B, C} -> Pure(A, [B, C]); {'BYTES_SPLIT', A, B, C} -> Pure(A, [B, C]);
{'BYTES_SPLIT_ANY', A, B, C} -> Pure(A, [B, C]);
{'BYTES_SIZE', A, B} -> Pure(A, B);
{'BYTES_TO_FIXED_SIZE', A, B, C} -> Pure(A, [B, C]);
{'ORACLE_CHECK', A, B, C, D} -> Pure(A, [B, C, D]); {'ORACLE_CHECK', A, B, C, D} -> Pure(A, [B, C, D]);
{'ORACLE_CHECK_QUERY', A, B, C, D, E} -> Pure(A, [B, C, D, E]); {'ORACLE_CHECK_QUERY', A, B, C, D, E} -> Pure(A, [B, C, D, E]);
{'IS_ORACLE', A, B} -> Pure(A, [B]); {'IS_ORACLE', A, B} -> Pure(A, [B]);
@ -998,6 +1096,7 @@ attributes(I) ->
{'MICROBLOCK', A} -> Pure(A, []); {'MICROBLOCK', A} -> Pure(A, []);
{'DIFFICULTY', A} -> Pure(A, []); {'DIFFICULTY', A} -> Pure(A, []);
{'GASLIMIT', A} -> Pure(A, []); {'GASLIMIT', A} -> Pure(A, []);
{'NETWORK_ID', A} -> Pure(A, []);
{'GAS', A} -> Pure(A, []); {'GAS', A} -> Pure(A, []);
{'LOG0', A} -> Impure(none, [A]); {'LOG0', A} -> Impure(none, [A]);
{'LOG1', A, B} -> Impure(none, [A, B]); {'LOG1', A, B} -> Impure(none, [A, B]);
@ -1082,11 +1181,16 @@ independent({i, _, I}, {i, _, J}) ->
StackI = lists:member(?a, [WI | RI]), StackI = lists:member(?a, [WI | RI]),
StackJ = lists:member(?a, [WJ | RJ]), StackJ = lists:member(?a, [WJ | RJ]),
if WI == pc; WJ == pc -> false; %% no jumps ReadStoreI = [] /= [ x || {store, _} <- RI ],
not (PureI or PureJ) -> false; %% at least one is pure ReadStoreJ = [] /= [ x || {store, _} <- RJ ],
StackI and StackJ -> false; %% cannot both use the stack
WI == WJ -> false; %% cannot write to the same register if WI == pc; WJ == pc -> false; %% no jumps
true -> not (PureI or PureJ) -> false; %% at least one is pure
StackI and StackJ -> false; %% cannot both use the stack
WI == WJ -> false; %% cannot write to the same register
ReadStoreI and not PureJ -> false; %% can't read store/state if other is impure
ReadStoreJ and not PureI -> false; %% can't read store/state if other is impure
true ->
%% and cannot write to each other's inputs %% and cannot write to each other's inputs
not lists:member(WI, RJ) andalso not lists:member(WI, RJ) andalso
not lists:member(WJ, RI) not lists:member(WJ, RI)
@ -1606,7 +1710,23 @@ bb(_Name, Code) ->
Blocks = lists:flatmap(fun split_calls/1, Blocks1), Blocks = lists:flatmap(fun split_calls/1, Blocks1),
Labels = maps:from_list([ {Ref, I} || {I, {Ref, _}} <- with_ixs(Blocks) ]), Labels = maps:from_list([ {Ref, I} || {I, {Ref, _}} <- with_ixs(Blocks) ]),
BBs = [ set_labels(Labels, B) || B <- Blocks ], BBs = [ set_labels(Labels, B) || B <- Blocks ],
maps:from_list(BBs). maps:from_list(dbg_loc_filter(BBs)).
%% Filter DBG_LOC instructions to keep one instruction per line
dbg_loc_filter(BBs) ->
dbg_loc_filter(BBs, [], [], sets:new()).
dbg_loc_filter([], _, AllBlocks, _) ->
lists:reverse(AllBlocks);
dbg_loc_filter([{I, []} | Rest], AllOps, AllBlocks, DbgLocs) ->
dbg_loc_filter(Rest, [], [{I, lists:reverse(AllOps)} | AllBlocks], DbgLocs);
dbg_loc_filter([{I, [Op = {'DBG_LOC', _, _} | Ops]} | Rest], AllOps, AllBlocks, DbgLocs) ->
case sets:is_element(Op, DbgLocs) of
true -> dbg_loc_filter([{I, Ops} | Rest], AllOps, AllBlocks, DbgLocs);
false -> dbg_loc_filter([{I, Ops} | Rest], [Op | AllOps], AllBlocks, sets:add_element(Op, DbgLocs))
end;
dbg_loc_filter([{I, [Op | Ops]} | Rest], AllOps, AllBlocks, DbgLocs) ->
dbg_loc_filter([{I, Ops} | Rest], [Op | AllOps], AllBlocks, DbgLocs).
%% -- Break up scode into basic blocks -- %% -- Break up scode into basic blocks --

View File

@ -16,7 +16,7 @@
many/1, many1/1, sep/2, sep1/2, many/1, many1/1, sep/2, sep1/2,
infixl/2, infixr/2]). infixl/2, infixr/2]).
-export([current_file/0, set_current_file/1, -export([current_file/0, set_current_file/1, current_dir/0, set_current_dir/1,
current_include_type/0, set_current_include_type/1]). current_include_type/0, set_current_include_type/1]).
%% -- Types ------------------------------------------------------------------ %% -- Types ------------------------------------------------------------------
@ -481,6 +481,13 @@ current_file() ->
set_current_file(File) -> set_current_file(File) ->
put('$current_file', File). put('$current_file', File).
%% Current source directory
current_dir() ->
get('$current_dir').
set_current_dir(File) ->
put('$current_dir', File).
add_current_file({L, C}) -> {current_file(), L, C}; add_current_file({L, C}) -> {current_file(), L, C};
add_current_file(Pos) -> Pos. add_current_file(Pos) -> Pos.

View File

@ -20,6 +20,7 @@
-include("aeso_parse_lib.hrl"). -include("aeso_parse_lib.hrl").
-import(aeso_parse_lib, [current_file/0, set_current_file/1, -import(aeso_parse_lib, [current_file/0, set_current_file/1,
current_dir/0, set_current_dir/1,
current_include_type/0, set_current_include_type/1]). current_include_type/0, set_current_include_type/1]).
-type parse_result() :: aeso_syntax:ast() | {aeso_syntax:ast(), sets:set(include_hash())} | none(). -type parse_result() :: aeso_syntax:ast() | {aeso_syntax:ast(), sets:set(include_hash())} | none().
@ -59,6 +60,7 @@ run_parser(P, Inp, Opts) ->
parse_and_scan(P, S, Opts) -> parse_and_scan(P, S, Opts) ->
set_current_file(proplists:get_value(src_file, Opts, no_file)), set_current_file(proplists:get_value(src_file, Opts, no_file)),
set_current_dir(proplists:get_value(src_dir, Opts, no_file)),
set_current_include_type(proplists:get_value(include_type, Opts, none)), set_current_include_type(proplists:get_value(include_type, Opts, none)),
case aeso_scan:scan(S) of case aeso_scan:scan(S) of
{ok, Tokens} -> aeso_parse_lib:parse(P, Tokens); {ok, Tokens} -> aeso_parse_lib:parse(P, Tokens);
@ -265,10 +267,11 @@ type300() ->
type400() -> type400() ->
choice( choice(
[?RULE(typeAtom(), optional(type_args()), [?RULE(typeAtom(), optional(type_args()),
case _2 of any_bytes(
none -> _1; case _2 of
{ok, Args} -> {app_t, get_ann(_1), _1, Args} none -> _1;
end), {ok, Args} -> {app_t, get_ann(_1), _1, Args}
end)),
?RULE(id("bytes"), parens(token(int)), ?RULE(id("bytes"), parens(token(int)),
{bytes_t, get_ann(_1), element(3, _2)}) {bytes_t, get_ann(_1), element(3, _2)})
]). ]).
@ -300,7 +303,7 @@ stmt() ->
, {switch, keyword(switch), parens(expr()), maybe_block(branch())} , {switch, keyword(switch), parens(expr()), maybe_block(branch())}
, {'if', keyword('if'), parens(expr()), body()} , {'if', keyword('if'), parens(expr()), body()}
, {elif, keyword(elif), parens(expr()), body()} , {elif, keyword(elif), parens(expr()), body()}
, {else, keyword(else), body()} , {'else', keyword('else'), body()}
])). ])).
branch() -> branch() ->
@ -324,7 +327,7 @@ expr100() ->
Expr150 = ?LAZY_P(expr150()), Expr150 = ?LAZY_P(expr150()),
choice( choice(
[ ?RULE(lam_args(), keyword('=>'), body(), {lam, _2, _1, _3}) %% TODO: better location [ ?RULE(lam_args(), keyword('=>'), body(), {lam, _2, _1, _3}) %% TODO: better location
, {'if', keyword('if'), parens(Expr100), Expr150, right(tok(else), Expr100)} , {'if', keyword('if'), parens(Expr100), Expr150, right(tok('else'), Expr100)}
, ?RULE(Expr150, optional(right(tok(':'), type())), , ?RULE(Expr150, optional(right(tok(':'), type())),
case _2 of case _2 of
none -> _1; none -> _1;
@ -334,14 +337,19 @@ expr100() ->
expr150() -> infixl(expr200(), binop('|>')). expr150() -> infixl(expr200(), binop('|>')).
expr200() -> infixr(expr300(), binop('||')). expr200() -> infixr(expr300(), binop('||')).
expr300() -> infixr(expr400(), binop('&&')). expr300() -> infixr(expr325(), binop('&&')).
expr325() -> infixl(expr350(), binop('bor')).
expr350() -> infixl(expr375(), binop('bxor')).
expr375() -> infixl(expr400(), binop('band')).
expr400() -> infix(expr500(), binop(['<', '>', '=<', '>=', '==', '!='])). expr400() -> infix(expr500(), binop(['<', '>', '=<', '>=', '==', '!='])).
expr500() -> infixr(expr600(), binop(['::', '++'])). expr500() -> infixr(expr550(), binop(['::', '++'])).
expr550() -> infixl(expr600(), binop(['<<', '>>'])).
expr600() -> infixl(expr650(), binop(['+', '-'])). expr600() -> infixl(expr650(), binop(['+', '-'])).
expr650() -> ?RULE(many(token('-')), expr700(), prefixes(_1, _2)). expr650() -> ?RULE(many(token('-')), expr700(), prefixes(_1, _2)).
expr700() -> infixl(expr750(), binop(['*', '/', mod])). expr700() -> infixl(expr750(), binop(['*', '/', mod])).
expr750() -> infixl(expr800(), binop(['^'])). expr750() -> infixl(expr800(), binop(['^'])).
expr800() -> ?RULE(many(token('!')), expr900(), prefixes(_1, _2)). expr800() -> ?RULE(many(token('!')), expr850(), prefixes(_1, _2)).
expr850() -> ?RULE(many(token('bnot')), expr900(), prefixes(_1, _2)).
expr900() -> ?RULE(exprAtom(), many(elim()), elim(_1, _2)). expr900() -> ?RULE(exprAtom(), many(elim()), elim(_1, _2)).
exprAtom() -> exprAtom() ->
@ -518,7 +526,7 @@ id_or_addr() ->
?RULE(id(), parse_addr_literal(_1)). ?RULE(id(), parse_addr_literal(_1)).
parse_addr_literal(Id = {id, Ann, Name}) -> parse_addr_literal(Id = {id, Ann, Name}) ->
case lists:member(lists:sublist(Name, 3), ["ak_", "ok_", "oq_", "ct_"]) of case lists:member(lists:sublist(Name, 3), ["ak_", "ok_", "oq_", "ct_", "sg_"]) of
false -> Id; false -> Id;
true -> true ->
try aeser_api_encoder:decode(list_to_binary(Name)) of try aeser_api_encoder:decode(list_to_binary(Name)) of
@ -557,6 +565,7 @@ bracket_list(P) -> brackets(comma_sep(P)).
-spec pos_ann(ann_line(), ann_col()) -> ann(). -spec pos_ann(ann_line(), ann_col()) -> ann().
pos_ann(Line, Col) -> pos_ann(Line, Col) ->
[ {file, current_file()} [ {file, current_file()}
, {dir, current_dir()}
, {include_type, current_include_type()} , {include_type, current_include_type()}
, {line, Line} , {line, Line}
, {col, Col} ]. , {col, Col} ].
@ -604,7 +613,7 @@ group_ifs([], Acc) ->
group_ifs([{'if', Ann, Cond, Then} | Stmts], Acc) -> group_ifs([{'if', Ann, Cond, Then} | Stmts], Acc) ->
{Elses, Rest} = else_branches(Stmts, []), {Elses, Rest} = else_branches(Stmts, []),
group_ifs(Rest, [build_if(Ann, Cond, Then, Elses) | Acc]); group_ifs(Rest, [build_if(Ann, Cond, Then, Elses) | Acc]);
group_ifs([{else, Ann, _} | _], _) -> group_ifs([{'else', Ann, _} | _], _) ->
fail({Ann, "No matching 'if' for 'else'"}); fail({Ann, "No matching 'if' for 'else'"});
group_ifs([{elif, Ann, _, _} | _], _) -> group_ifs([{elif, Ann, _, _} | _], _) ->
fail({Ann, "No matching 'if' for 'elif'"}); fail({Ann, "No matching 'if' for 'elif'"});
@ -614,14 +623,14 @@ group_ifs([Stmt | Stmts], Acc) ->
build_if(Ann, Cond, Then, [{elif, Ann1, Cond1, Then1} | Elses]) -> build_if(Ann, Cond, Then, [{elif, Ann1, Cond1, Then1} | Elses]) ->
{'if', Ann, Cond, Then, {'if', Ann, Cond, Then,
set_ann(format, elif, build_if(Ann1, Cond1, Then1, Elses))}; set_ann(format, elif, build_if(Ann1, Cond1, Then1, Elses))};
build_if(Ann, Cond, Then, [{else, _Ann, Else}]) -> build_if(Ann, Cond, Then, [{'else', _Ann, Else}]) ->
{'if', Ann, Cond, Then, Else}; {'if', Ann, Cond, Then, Else};
build_if(Ann, Cond, Then, []) -> build_if(Ann, Cond, Then, []) ->
{'if', Ann, Cond, Then, {tuple, [{origin, system}], []}}. {'if', Ann, Cond, Then, {tuple, [{origin, system}], []}}.
else_branches([Elif = {elif, _, _, _} | Stmts], Acc) -> else_branches([Elif = {elif, _, _, _} | Stmts], Acc) ->
else_branches(Stmts, [Elif | Acc]); else_branches(Stmts, [Elif | Acc]);
else_branches([Else = {else, _, _} | Stmts], Acc) -> else_branches([Else = {'else', _, _} | Stmts], Acc) ->
{lists:reverse([Else | Acc]), Stmts}; {lists:reverse([Else | Acc]), Stmts};
else_branches(Stmts, Acc) -> else_branches(Stmts, Acc) ->
{lists:reverse(Acc), Stmts}. {lists:reverse(Acc), Stmts}.
@ -697,7 +706,7 @@ expand_includes([], Included, Acc, Opts) ->
end; end;
expand_includes([{include, Ann, {string, _SAnn, File}} | AST], Included, Acc, Opts) -> expand_includes([{include, Ann, {string, _SAnn, File}} | AST], Included, Acc, Opts) ->
case get_include_code(File, Ann, Opts) of case get_include_code(File, Ann, Opts) of
{ok, Code} -> {ok, AbsDir, Code} ->
Hashed = hash_include(File, Code), Hashed = hash_include(File, Code),
case sets:is_element(Hashed, Included) of case sets:is_element(Hashed, Included) of
false -> false ->
@ -707,9 +716,10 @@ expand_includes([{include, Ann, {string, _SAnn, File}} | AST], Included, Acc, Op
_ -> indirect _ -> indirect
end, end,
Opts1 = lists:keystore(src_file, 1, Opts, {src_file, File}), Opts1 = lists:keystore(src_file, 1, Opts, {src_file, File}),
Opts2 = lists:keystore(include_type, 1, Opts1, {include_type, IncludeType}), Opts2 = lists:keystore(src_dir, 1, Opts1, {src_dir, AbsDir}),
Opts3 = lists:keystore(include_type, 1, Opts2, {include_type, IncludeType}),
Included1 = sets:add_element(Hashed, Included), Included1 = sets:add_element(Hashed, Included),
case parse_and_scan(file(), Code, Opts2) of case parse_and_scan(file(), Code, Opts3) of
{ok, AST1} -> {ok, AST1} ->
expand_includes(AST1 ++ AST, Included1, Acc, Opts); expand_includes(AST1 ++ AST, Included1, Acc, Opts);
Err = {error, _} -> Err = {error, _} ->
@ -727,13 +737,12 @@ expand_includes([E | AST], Included, Acc, Opts) ->
read_file(File, Opts) -> read_file(File, Opts) ->
case proplists:get_value(include, Opts, {explicit_files, #{}}) of case proplists:get_value(include, Opts, {explicit_files, #{}}) of
{file_system, Paths} -> {file_system, Paths} ->
CandidateNames = [ filename:join(Dir, File) || Dir <- Paths ], lists:foldr(fun(Path, {error, _}) -> read_file_(Path, File);
lists:foldr(fun(F, {error, _}) -> file:read_file(F); (_Path, OK) -> OK end, {error, not_found}, Paths);
(_F, OK) -> OK end, {error, not_found}, CandidateNames);
{explicit_files, Files} -> {explicit_files, Files} ->
case maps:get(binary_to_list(File), Files, not_found) of case maps:get(binary_to_list(File), Files, not_found) of
not_found -> {error, not_found}; not_found -> {error, not_found};
Src -> {ok, Src} Src -> {ok, File, Src}
end; end;
escript -> escript ->
try try
@ -742,7 +751,7 @@ read_file(File, Opts) ->
Archive = proplists:get_value(archive, Sections), Archive = proplists:get_value(archive, Sections),
FileName = binary_to_list(filename:join([aesophia, priv, stdlib, File])), FileName = binary_to_list(filename:join([aesophia, priv, stdlib, File])),
case zip:extract(Archive, [{file_list, [FileName]}, memory]) of case zip:extract(Archive, [{file_list, [FileName]}, memory]) of
{ok, [{_, Src}]} -> {ok, Src}; {ok, [{_, Src}]} -> {ok, escript, Src};
_ -> {error, not_found} _ -> {error, not_found}
end end
catch _:_ -> catch _:_ ->
@ -750,6 +759,13 @@ read_file(File, Opts) ->
end end
end. end.
read_file_(Path, File) ->
AbsFile = filename:join(Path, File),
case file:read_file(AbsFile) of
{ok, Bin} -> {ok, aeso_utils:canonical_dir(filename:dirname(AbsFile)), Bin};
Err -> Err
end.
stdlib_options() -> stdlib_options() ->
StdLibDir = aeso_stdlib:stdlib_include_path(), StdLibDir = aeso_stdlib:stdlib_include_path(),
case filelib:is_dir(StdLibDir) of case filelib:is_dir(StdLibDir) of
@ -758,23 +774,37 @@ stdlib_options() ->
end. end.
get_include_code(File, Ann, Opts) -> get_include_code(File, Ann, Opts) ->
case {read_file(File, Opts), read_file(File, stdlib_options())} of %% Temporarily extend include paths with the directory of the current file
{{ok, Bin}, {ok, _}} -> Opts1 = include_current_file_dir(Opts, Ann),
case {read_file(File, Opts1), read_file(File, stdlib_options())} of
{{ok, Dir, Bin}, {ok, _}} ->
case filename:basename(File) == File of case filename:basename(File) == File of
true -> { error true -> { error
, fail( ann_pos(Ann) , fail( ann_pos(Ann)
, "Illegal redefinition of standard library " ++ binary_to_list(File))}; , "Illegal redefinition of standard library " ++ binary_to_list(File))};
%% If a path is provided then the stdlib takes lower priority %% If a path is provided then the stdlib takes lower priority
false -> {ok, binary_to_list(Bin)} false -> {ok, Dir, binary_to_list(Bin)}
end; end;
{_, {ok, Bin}} -> {_, {ok, _, Bin}} ->
{ok, binary_to_list(Bin)}; {ok, stdlib, binary_to_list(Bin)};
{{ok, Bin}, _} -> {{ok, Dir, Bin}, _} ->
{ok, binary_to_list(Bin)}; {ok, Dir, binary_to_list(Bin)};
{_, _} -> {_, _} ->
{error, {ann_pos(Ann), include_error, File}} {error, {ann_pos(Ann), include_error, File}}
end. end.
include_current_file_dir(Opts, Ann) ->
case {proplists:get_value(dir, Ann, undefined),
proplists:get_value(include, Opts, undefined)} of
{undefined, _} -> Opts;
{CurrDir, {file_system, Paths}} ->
case lists:member(CurrDir, Paths) of
false -> [{include, {file_system, [CurrDir | Paths]}} | Opts];
true -> Opts
end;
{_, _} -> Opts
end.
-spec hash_include(string() | binary(), string()) -> include_hash(). -spec hash_include(string() | binary(), string()) -> include_hash().
hash_include(File, Code) when is_binary(File) -> hash_include(File, Code) when is_binary(File) ->
hash_include(binary_to_list(File), Code); hash_include(binary_to_list(File), Code);
@ -788,3 +818,7 @@ auto_imports(L) when is_list(L) ->
auto_imports(T) when is_tuple(T) -> auto_imports(T) when is_tuple(T) ->
auto_imports(tuple_to_list(T)); auto_imports(tuple_to_list(T));
auto_imports(_) -> []. auto_imports(_) -> [].
any_bytes({id, Ann, "bytes"}) -> {bytes_t, Ann, any};
any_bytes({app_t, _, {id, Ann, "bytes"}, []}) -> {bytes_t, Ann, any};
any_bytes(Type) -> Type.

View File

@ -276,7 +276,9 @@ type({tuple_t, _, Args}) ->
tuple_type(Args); tuple_type(Args);
type({args_t, _, Args}) -> type({args_t, _, Args}) ->
args_type(Args); args_type(Args);
type({bytes_t, _, any}) -> text("bytes(_)"); type({bytes_t, _, any}) -> text("bytes()");
type({bytes_t, _, '_'}) -> text("bytes(_)");
type({bytes_t, _, fixed}) -> text("bytes(_)");
type({bytes_t, _, Len}) -> type({bytes_t, _, Len}) ->
text(lists:concat(["bytes(", Len, ")"])); text(lists:concat(["bytes(", Len, ")"]));
type({if_t, _, Id, Then, Else}) -> type({if_t, _, Id, Then, Else}) ->
@ -386,7 +388,8 @@ expr_p(_, {Type, _, Bin})
when Type == account_pubkey; when Type == account_pubkey;
Type == contract_pubkey; Type == contract_pubkey;
Type == oracle_pubkey; Type == oracle_pubkey;
Type == oracle_query_id -> Type == oracle_query_id;
Type == signature ->
text(binary_to_list(aeser_api_encoder:encode(Type, Bin))); text(binary_to_list(aeser_api_encoder:encode(Type, Bin)));
expr_p(_, {string, _, <<>>}) -> text("\"\""); expr_p(_, {string, _, <<>>}) -> text("\"\"");
expr_p(_, {string, _, S}) -> expr_p(_, {string, _, S}) ->
@ -417,7 +420,7 @@ stmt_p({'if', _, Cond, Then}) ->
block_expr(200, beside(text("if"), paren(expr(Cond))), Then); block_expr(200, beside(text("if"), paren(expr(Cond))), Then);
stmt_p({elif, _, Cond, Then}) -> stmt_p({elif, _, Cond, Then}) ->
block_expr(200, beside(text("elif"), paren(expr(Cond))), Then); block_expr(200, beside(text("elif"), paren(expr(Cond))), Then);
stmt_p({else, Else}) -> stmt_p({'else', Else}) ->
HideGenerated = not show_generated(), HideGenerated = not show_generated(),
case aeso_syntax:get_ann(origin, Else) of case aeso_syntax:get_ann(origin, Else) of
system when HideGenerated -> empty(); system when HideGenerated -> empty();
@ -437,15 +440,20 @@ bin_prec('=') -> { 0, 0, 0}; %% Always printed inside '[ ]'
bin_prec('@') -> { 0, 0, 0}; %% Only in error messages bin_prec('@') -> { 0, 0, 0}; %% Only in error messages
bin_prec('|>') -> {150, 150, 200}; bin_prec('|>') -> {150, 150, 200};
bin_prec('||') -> {200, 300, 200}; bin_prec('||') -> {200, 300, 200};
bin_prec('&&') -> {300, 400, 300}; bin_prec('&&') -> {300, 325, 300};
bin_prec('bor') -> {325, 350, 325};
bin_prec('bxor') -> {350, 375, 350};
bin_prec('band') -> {375, 400, 375};
bin_prec('<') -> {400, 500, 500}; bin_prec('<') -> {400, 500, 500};
bin_prec('>') -> {400, 500, 500}; bin_prec('>') -> {400, 500, 500};
bin_prec('=<') -> {400, 500, 500}; bin_prec('=<') -> {400, 500, 500};
bin_prec('>=') -> {400, 500, 500}; bin_prec('>=') -> {400, 500, 500};
bin_prec('==') -> {400, 500, 500}; bin_prec('==') -> {400, 500, 500};
bin_prec('!=') -> {400, 500, 500}; bin_prec('!=') -> {400, 500, 500};
bin_prec('++') -> {500, 600, 500}; bin_prec('++') -> {500, 550, 500};
bin_prec('::') -> {500, 600, 500}; bin_prec('::') -> {500, 550, 500};
bin_prec('<<') -> {550, 600, 550};
bin_prec('>>') -> {550, 600, 550};
bin_prec('+') -> {600, 600, 650}; bin_prec('+') -> {600, 600, 650};
bin_prec('-') -> {600, 600, 650}; bin_prec('-') -> {600, 600, 650};
bin_prec('*') -> {700, 700, 750}; bin_prec('*') -> {700, 700, 750};
@ -455,7 +463,8 @@ bin_prec('^') -> {750, 750, 800}.
-spec un_prec(aeso_syntax:un_op()) -> {integer(), integer()}. -spec un_prec(aeso_syntax:un_op()) -> {integer(), integer()}.
un_prec('-') -> {650, 650}; un_prec('-') -> {650, 650};
un_prec('!') -> {800, 800}. un_prec('!') -> {800, 800};
un_prec('bnot') -> {850, 850}.
equals(Ann, A, B) -> equals(Ann, A, B) ->
{app, [{format, infix} | Ann], {'=', Ann}, [A, B]}. {app, [{format, infix} | Ann], {'=', Ann}, [A, B]}.
@ -526,5 +535,5 @@ get_elifs(If = {'if', Ann, Cond, Then, Else}, Elifs) ->
elif -> get_elifs(Else, [{elif, Ann, Cond, Then} | Elifs]); elif -> get_elifs(Else, [{elif, Ann, Cond, Then} | Elifs]);
_ -> {lists:reverse(Elifs), If} _ -> {lists:reverse(Elifs), If}
end; end;
get_elifs(Else, Elifs) -> {lists:reverse(Elifs), {else, Else}}. get_elifs(Else, Elifs) -> {lists:reverse(Elifs), {'else', Else}}.

View File

@ -46,7 +46,7 @@ lexer() ->
Keywords = ["contract", "include", "let", "switch", "type", "record", "datatype", "if", "elif", "else", "function", Keywords = ["contract", "include", "let", "switch", "type", "record", "datatype", "if", "elif", "else", "function",
"stateful", "payable", "true", "false", "mod", "public", "entrypoint", "private", "indexed", "namespace", "stateful", "payable", "true", "false", "mod", "public", "entrypoint", "private", "indexed", "namespace",
"interface", "main", "using", "as", "for", "hiding" "interface", "main", "using", "as", "for", "hiding", "band", "bor", "bxor", "bnot"
], ],
KW = string:join(Keywords, "|"), KW = string:join(Keywords, "|"),

View File

@ -11,7 +11,7 @@
-export([get_ann/1, get_ann/2, get_ann/3, set_ann/2, qualify/2]). -export([get_ann/1, get_ann/2, get_ann/3, set_ann/2, qualify/2]).
-export_type([ann_line/0, ann_col/0, ann_origin/0, ann_format/0, ann/0]). -export_type([ann_file/0, ann_line/0, ann_col/0, ann_origin/0, ann_format/0, ann/0]).
-export_type([name/0, id/0, con/0, qid/0, qcon/0, tvar/0, op/0]). -export_type([name/0, id/0, con/0, qid/0, qcon/0, tvar/0, op/0]).
-export_type([bin_op/0, un_op/0]). -export_type([bin_op/0, un_op/0]).
-export_type([decl/0, letbind/0, typedef/0, pragma/0, fundecl/0]). -export_type([decl/0, letbind/0, typedef/0, pragma/0, fundecl/0]).
@ -25,8 +25,9 @@
-type ann_col() :: integer(). -type ann_col() :: integer().
-type ann_origin() :: system | user. -type ann_origin() :: system | user.
-type ann_format() :: '?:' | hex | infix | prefix | elif. -type ann_format() :: '?:' | hex | infix | prefix | elif.
-type ann_file() :: string() | no_file.
-type ann() :: [ {line, ann_line()} | {col, ann_col()} | {format, ann_format()} | {origin, ann_origin()} -type ann() :: [ {file, ann_file()} | {line, ann_line()} | {col, ann_col()} | {format, ann_format()} | {origin, ann_origin()}
| stateful | private | payable | main | interface | entrypoint]. | stateful | private | payable | main | interface | entrypoint].
-type name() :: string(). -type name() :: string().
@ -100,6 +101,7 @@
| {contract_pubkey, ann(), binary()} | {contract_pubkey, ann(), binary()}
| {oracle_pubkey, ann(), binary()} | {oracle_pubkey, ann(), binary()}
| {oracle_query_id, ann(), binary()} | {oracle_query_id, ann(), binary()}
| {signature, ann(), binary()}
| {string, ann(), binary()} | {string, ann(), binary()}
| {char, ann(), integer()}. | {char, ann(), integer()}.
@ -107,8 +109,8 @@
-type bin_op() :: '+' | '-' | '*' | '/' | mod | '^' -type bin_op() :: '+' | '-' | '*' | '/' | mod | '^'
| '++' | '::' | '<' | '>' | '=<' | '>=' | '==' | '!=' | '++' | '::' | '<' | '>' | '=<' | '>=' | '==' | '!='
| '||' | '&&' | '..' | '|>'. | '||' | '&&' | '..' | 'band' | 'bor' | 'bxor' | '>>' | '<<' | '|>'.
-type un_op() :: '-' | '!'. -type un_op() :: '-' | '!' | 'bnot'.
-type expr() -type expr()
:: {lam, ann(), [arg()], expr()} :: {lam, ann(), [arg()], expr()}

View File

@ -7,7 +7,7 @@
-module(aeso_syntax_utils). -module(aeso_syntax_utils).
-vsn("7.1.2"). -vsn("7.1.2").
-export([used_ids/1, used_types/2, used/1]). -export([used_ids/1, used_ids/2, used_types/2, used/1]).
-record(alg, {zero, plus, scoped}). -record(alg, {zero, plus, scoped}).
@ -111,8 +111,16 @@ fold(Alg = #alg{zero = Zero, plus = Plus, scoped = Scoped}, Fun, K, X) ->
%% Name dependencies %% Name dependencies
%% Used ids, top level
used_ids(E) -> used_ids(E) ->
[ X || {{term, [X]}, _} <- used(E) ]. used_ids([], E).
%% Used ids, top level or in (current) namespace
used_ids(Ns, E) ->
[ lists:last(Xs) || {{term, Xs}, _} <- used(E), in_ns(Xs, Ns) ].
in_ns([_], _) -> true;
in_ns(Xs, Ns) -> lists:droplast(Xs) == Ns.
used_types([Top] = _CurrentNS, T) -> used_types([Top] = _CurrentNS, T) ->
F = fun({{type, [X]}, _}) -> [X]; F = fun({{type, [X]}, _}) -> [X];

View File

@ -7,10 +7,22 @@
-module(aeso_utils). -module(aeso_utils).
-vsn("7.1.2"). -vsn("7.1.2").
-export([scc/1]). -export([scc/1, canonical_dir/1]).
-export_type([graph/1]). -export_type([graph/1]).
%% -- Simplistic canonical directory
%% Note: no attempts to be 100% complete
canonical_dir(Dir) ->
{ok, Cwd} = file:get_cwd(),
AbsName = filename:absname(Dir),
RelAbsName = filename:join(tl(filename:split(AbsName))),
case filelib:safe_relative_path(RelAbsName, Cwd) of
unsafe -> AbsName;
Simplified -> filename:absname(Simplified, "")
end.
%% -- Topological sort %% -- Topological sort
-type graph(Node) :: #{Node => [Node]}. %% List of incoming edges (dependencies). -type graph(Node) :: #{Node => [Node]}. %% List of incoming edges (dependencies).

View File

@ -13,9 +13,13 @@
-spec from_fate(aeso_syntax:type(), aeb_fate_data:fate_type()) -> aeso_syntax:expr(). -spec from_fate(aeso_syntax:type(), aeb_fate_data:fate_type()) -> aeso_syntax:expr().
from_fate({id, _, "address"}, ?FATE_ADDRESS(Bin)) -> {account_pubkey, [], Bin}; from_fate({id, _, "address"}, ?FATE_ADDRESS(Bin)) -> {account_pubkey, [], Bin};
from_fate({id, _, "signature"}, ?FATE_BYTES(Bin)) -> {signature, [], Bin};
from_fate({id, _, "hash"}, ?FATE_BYTES(Bin)) -> {bytes, [], Bin};
from_fate({id, _, "unit"}, ?FATE_UNIT) -> {tuple, [], []};
from_fate({app_t, _, {id, _, "oracle"}, _}, ?FATE_ORACLE(Bin)) -> {oracle_pubkey, [], Bin}; from_fate({app_t, _, {id, _, "oracle"}, _}, ?FATE_ORACLE(Bin)) -> {oracle_pubkey, [], Bin};
from_fate({app_t, _, {id, _, "oracle_query"}, _}, ?FATE_ORACLE_Q(Bin)) -> {oracle_query_id, [], Bin}; from_fate({app_t, _, {id, _, "oracle_query"}, _}, ?FATE_ORACLE_Q(Bin)) -> {oracle_query_id, [], Bin};
from_fate({con, _, _Name}, ?FATE_CONTRACT(Bin)) -> {contract_pubkey, [], Bin}; from_fate({con, _, _Name}, ?FATE_CONTRACT(Bin)) -> {contract_pubkey, [], Bin};
from_fate({bytes_t, _, any}, ?FATE_BYTES(Bin)) -> make_any_bytes(Bin);
from_fate({bytes_t, _, N}, ?FATE_BYTES(Bin)) when byte_size(Bin) == N -> {bytes, [], Bin}; from_fate({bytes_t, _, N}, ?FATE_BYTES(Bin)) when byte_size(Bin) == N -> {bytes, [], Bin};
from_fate({id, _, "bits"}, ?FATE_BITS(N)) -> make_bits(N); from_fate({id, _, "bits"}, ?FATE_BITS(N)) -> make_bits(N);
from_fate({id, _, "int"}, N) when is_integer(N) -> from_fate({id, _, "int"}, N) when is_integer(N) ->
@ -79,6 +83,7 @@ from_fate_builtin(QType, Val) ->
Hsh = {bytes_t, [], 32}, Hsh = {bytes_t, [], 32},
I32 = {bytes_t, [], 32}, I32 = {bytes_t, [], 32},
I48 = {bytes_t, [], 48}, I48 = {bytes_t, [], 48},
Bts = {bytes_t, [], any},
Qid = fun(Name) -> {qid, [], Name} end, Qid = fun(Name) -> {qid, [], Name} end,
Map = fun(KT, VT) -> {app_t, [], {id, [], "map"}, [KT, VT]} end, Map = fun(KT, VT) -> {app_t, [], {id, [], "map"}, [KT, VT]} end,
ChainTxArities = [3, 0, 0, 0, 0, 0, 1, 1, 1, 2, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 2, 0], ChainTxArities = [3, 0, 0, 0, 0, 0, 1, 1, 1, 2, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 2, 0],
@ -89,7 +94,7 @@ from_fate_builtin(QType, Val) ->
{["AENS", "name"], {variant, [3], 0, {Addr, TTL, Ptrs}}} -> {["AENS", "name"], {variant, [3], 0, {Addr, TTL, Ptrs}}} ->
App(["AENS","Name"], [Chk(Adr, Addr), Chk(Qid(["Chain", "ttl"]), TTL), App(["AENS","Name"], [Chk(Adr, Addr), Chk(Qid(["Chain", "ttl"]), TTL),
Chk(Map(Str, Qid(["AENS", "pointee"])), Ptrs)]); Chk(Map(Str, Qid(["AENS", "pointee"])), Ptrs)]);
{["AENS", "pointee"], {variant, [1, 1, 1, 1], 0, {Addr}}} -> {["AENS", "pointee"], {variant, [1, 1, 1, 1], 0, {Addr}}} ->
App(["AENS","AccountPt"], [Chk(Adr, Addr)]); App(["AENS","AccountPt"], [Chk(Adr, Addr)]);
@ -100,6 +105,21 @@ from_fate_builtin(QType, Val) ->
{["AENS", "pointee"], {variant, [1, 1, 1, 1], 3, {Addr}}} -> {["AENS", "pointee"], {variant, [1, 1, 1, 1], 3, {Addr}}} ->
App(["AENS","ChannelPt"], [Chk(Adr, Addr)]); App(["AENS","ChannelPt"], [Chk(Adr, Addr)]);
{["AENSv2", "name"], {variant, [3], 0, {Addr, TTL, Ptrs}}} ->
App(["AENSv2","Name"], [Chk(Adr, Addr), Chk(Qid(["Chain", "ttl"]), TTL),
Chk(Map(Str, Qid(["AENSv2", "pointee"])), Ptrs)]);
{["AENSv2", "pointee"], {variant, [1, 1, 1, 1, 1], 0, {Value}}} ->
App(["AENSv2","AccountPt"], [Chk(Adr, Value)]);
{["AENSv2", "pointee"], {variant, [1, 1, 1, 1, 1], 1, {Value}}} ->
App(["AENSv2","OraclePt"], [Chk(Adr, Value)]);
{["AENSv2", "pointee"], {variant, [1, 1, 1, 1, 1], 2, {Value}}} ->
App(["AENSv2","ContractPt"], [Chk(Adr, Value)]);
{["AENSv2", "pointee"], {variant, [1, 1, 1, 1, 1], 3, {Value}}} ->
App(["AENSv2","ChannelPt"], [Chk(Adr, Value)]);
{["AENSv2", "pointee"], {variant, [1, 1, 1, 1, 1], 4, {Value}}} ->
App(["AENSv2","DataPt"], [Chk(Bts, Value)]);
{["Chain", "ga_meta_tx"], {variant, [2], 0, {Addr, X}}} -> {["Chain", "ga_meta_tx"], {variant, [2], 0, {Addr, X}}} ->
App(["Chain","GAMetaTx"], [Chk(Adr, Addr), Chk(Int, X)]); App(["Chain","GAMetaTx"], [Chk(Adr, Addr), Chk(Int, X)]);
@ -171,3 +191,5 @@ make_bits(Set, Zero, I, N) when 0 == N rem 2 ->
make_bits(Set, Zero, I, N) -> make_bits(Set, Zero, I, N) ->
{app, [], Set, [make_bits(Set, Zero, I + 1, N div 2), {int, [], I}]}. {app, [], Set, [make_bits(Set, Zero, I + 1, N div 2), {int, [], I}]}.
make_any_bytes(Bin) ->
{app, [], {qid, [], ["Bytes", "to_any_size"]}, [{bytes, [], Bin}]}.

View File

@ -1,13 +1,12 @@
{application, aesophia, {application, aesophia,
[{description, "Compiler for Aeternity Sophia language"}, [{description, "Compiler for Aeternity Sophia language"},
{vsn, "7.1.0"}, {vsn, "8.0.1"},
{registered, []}, {registered, []},
{applications, {applications,
[kernel, [kernel,
stdlib, stdlib,
jsx, jsx,
syntax_tools, syntax_tools,
getopt,
aebytecode, aebytecode,
eblake2 eblake2
]}, ]},

View File

@ -85,6 +85,7 @@ compilable_contracts() ->
{"funargs", "bitsum", ["Bits.clear(Bits.clear(Bits.all, 4), 2)"]}, %% Order matters for test {"funargs", "bitsum", ["Bits.clear(Bits.clear(Bits.all, 4), 2)"]}, %% Order matters for test
{"funargs", "bitsum", ["Bits.set(Bits.set(Bits.none, 4), 2)"]}, {"funargs", "bitsum", ["Bits.set(Bits.set(Bits.none, 4), 2)"]},
{"funargs", "read", ["{label = \"question 1\", result = 4}"]}, {"funargs", "read", ["{label = \"question 1\", result = 4}"]},
{"funargs", "any_bytes", ["Bytes.to_any_size(#0011AA)"]},
{"funargs", "sjutton", ["#0011012003100011012003100011012003"]}, {"funargs", "sjutton", ["#0011012003100011012003100011012003"]},
{"funargs", "sextiosju", ["#01020304050607080910111213141516171819202122232425262728293031323334353637383940" {"funargs", "sextiosju", ["#01020304050607080910111213141516171819202122232425262728293031323334353637383940"
"414243444546474849505152535455565758596061626364656667"]}, "414243444546474849505152535455565758596061626364656667"]},
@ -116,6 +117,7 @@ compilable_contracts() ->
{"funargs", "chain_base_tx", ["Chain.NameRevokeTx(#ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)"]}, {"funargs", "chain_base_tx", ["Chain.NameRevokeTx(#ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)"]},
{"funargs", "chain_base_tx", ["Chain.NameTransferTx(ak_2dATVcZ9KJU5a8hdsVtTv21pYiGWiPbmVcU1Pz72FFqpk9pSRR, #ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)"]}, {"funargs", "chain_base_tx", ["Chain.NameTransferTx(ak_2dATVcZ9KJU5a8hdsVtTv21pYiGWiPbmVcU1Pz72FFqpk9pSRR, #ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)"]},
{"funargs", "chain_base_tx", ["Chain.GAAttachTx"]}, {"funargs", "chain_base_tx", ["Chain.GAAttachTx"]},
{"funargs", "sig", ["sg_MhibzTP1wWzGCTjtPFr1TiPqRJrrJqw7auvEuF5i3FdoALWqXLBDY6xxRRNUSPHK3EQTnTzF12EyspkxrSMxVHKsZeSMj"]},
{"variant_types", "init", []}, {"variant_types", "init", []},
{"basic_auth", "init", []}, {"basic_auth", "init", []},
{"address_literals", "init", []}, {"address_literals", "init", []},

View File

@ -70,6 +70,7 @@ simple_compile_test_() ->
fun() -> fun() ->
#{ warnings := Warnings } = compile("warnings", [warn_all]), #{ warnings := Warnings } = compile("warnings", [warn_all]),
#{ warnings := [] } = compile("warning_unused_include_no_include", [warn_all]), #{ warnings := [] } = compile("warning_unused_include_no_include", [warn_all]),
#{ warnings := [] } = compile("warning_used_record_typedef", [warn_all]),
check_warnings(warnings(), Warnings) check_warnings(warnings(), Warnings)
end} ] ++ end} ] ++
[]. [].
@ -161,6 +162,7 @@ compilable_contracts() ->
"state_handling", "state_handling",
"events", "events",
"include", "include",
"relative_include",
"basic_auth", "basic_auth",
"basic_auth_tx", "basic_auth_tx",
"bitcoin_auth", "bitcoin_auth",
@ -170,6 +172,7 @@ compilable_contracts() ->
"namespace_bug", "namespace_bug",
"bytes_to_x", "bytes_to_x",
"bytes_concat", "bytes_concat",
"bytes_misc",
"aens", "aens",
"aens_update", "aens_update",
"tuple_match", "tuple_match",
@ -223,6 +226,7 @@ compilable_contracts() ->
"unapplied_named_arg_builtin", "unapplied_named_arg_builtin",
"resolve_field_constraint_by_arity", "resolve_field_constraint_by_arity",
"toplevel_constants", "toplevel_constants",
"ceres",
"test" % Custom general-purpose test file. Keep it last on the list. "test" % Custom general-purpose test file. Keep it last on the list.
]. ].
@ -447,6 +451,10 @@ failing_contracts() ->
[<<?Pos(12, 42) [<<?Pos(12, 42)
"Cannot unify `int` and `string`\n" "Cannot unify `int` and `string`\n"
"when checking the type of the expression `r.foo() : map(int, string)` " "when checking the type of the expression `r.foo() : map(int, string)` "
"against the expected type `map(string, int)`">>,
<<?Pos(12, 42)
"Cannot unify `string` and `int`\n"
"when checking the type of the expression `r.foo() : map(int, string)` "
"against the expected type `map(string, int)`">>]) "against the expected type `map(string, int)`">>])
, ?TYPE_ERROR(not_toplevel_include, , ?TYPE_ERROR(not_toplevel_include,
[<<?Pos(2, 11) [<<?Pos(2, 11)
@ -604,6 +612,21 @@ failing_contracts() ->
[<<?Pos(3, 5) [<<?Pos(3, 5)
"Unbound variable `Chain.event`\n" "Unbound variable `Chain.event`\n"
"Did you forget to define the event type?">>]) "Did you forget to define the event type?">>])
, ?TYPE_ERROR(bad_bytes_to_x,
[<<?Pos(3, 35)
"Cannot resolve length of byte array in\n"
" the result of a call to Bytes.to_fixed_size">>,
<<?Pos(4, 36)
"Cannot unify `bytes()` and `bytes(4)`\nwhen checking the application of\n"
" `Bytes.to_fixed_size : (bytes()) => option('a)`\n"
"to arguments\n"
" `b : bytes(4)`">>,
<<?Pos(4, 36)
"Cannot resolve length of byte array in\n"
" the result of a call to Bytes.to_fixed_size">>,
<<?Pos(5, 35)
"Cannot resolve length of byte array in\n"
" the first argument of a call to Bytes.to_any_size">>])
, ?TYPE_ERROR(bad_bytes_concat, , ?TYPE_ERROR(bad_bytes_concat,
[<<?Pos(12, 40) [<<?Pos(12, 40)
"Failed to resolve byte array lengths in call to Bytes.concat with arguments of type\n" "Failed to resolve byte array lengths in call to Bytes.concat with arguments of type\n"
@ -628,7 +651,8 @@ failing_contracts() ->
"and result type\n" "and result type\n"
" - 'c (at line 16, column 39)">>, " - 'c (at line 16, column 39)">>,
<<?Pos(19, 25) <<?Pos(19, 25)
"Cannot resolve length of byte array.">>]) "Cannot resolve type of byte array in\n"
" the first argument of a call to Bytes.to_str">>])
, ?TYPE_ERROR(bad_bytes_split, , ?TYPE_ERROR(bad_bytes_split,
[<<?Pos(13, 5) [<<?Pos(13, 5)
"Failed to resolve byte array lengths in call to Bytes.split with argument of type\n" "Failed to resolve byte array lengths in call to Bytes.split with argument of type\n"
@ -871,10 +895,10 @@ failing_contracts() ->
"Trying to implement or extend an undefined interface `Z`">> "Trying to implement or extend an undefined interface `Z`">>
]) ])
, ?TYPE_ERROR(polymorphism_contract_interface_same_name_different_type, , ?TYPE_ERROR(polymorphism_contract_interface_same_name_different_type,
[<<?Pos(9,5) [<<?Pos(5,5)
"Duplicate definitions of `f` at\n" "Cannot unify `char` and `int`\n"
" - line 8, column 5\n" "when implementing the entrypoint `f` from the interface `I1`">>
" - line 9, column 5">>]) ])
, ?TYPE_ERROR(polymorphism_contract_missing_implementation, , ?TYPE_ERROR(polymorphism_contract_missing_implementation,
[<<?Pos(4,20) [<<?Pos(4,20)
"Unimplemented entrypoint `f` from the interface `I1` in the contract `I2`">> "Unimplemented entrypoint `f` from the interface `I1` in the contract `I2`">>
@ -928,6 +952,9 @@ failing_contracts() ->
<<?Pos(67,36) <<?Pos(67,36)
"Cannot unify `Cat` and `Animal` in a contravariant context\n" "Cannot unify `Cat` and `Animal` in a contravariant context\n"
"when checking the application of\n `DT_INV : ((Cat) => Cat) => dt_inv(Cat)`\nto arguments\n `f_c_to_a : (Cat) => Animal`">>, "when checking the application of\n `DT_INV : ((Cat) => Cat) => dt_inv(Cat)`\nto arguments\n `f_c_to_a : (Cat) => Animal`">>,
<<?Pos(67,36)
"Cannot unify `Cat` and `Animal` in a invariant context\n"
"when checking the type of the expression `DT_INV(f_c_to_a) : dt_inv(Cat)` against the expected type `dt_inv(Animal)`">>,
<<?Pos(68,36) <<?Pos(68,36)
"Cannot unify `Cat` and `Animal` in a invariant context\n" "Cannot unify `Cat` and `Animal` in a invariant context\n"
"when checking the type of the expression `DT_INV(f_c_to_c) : dt_inv(Cat)` against the expected type `dt_inv(Animal)`">>, "when checking the type of the expression `DT_INV(f_c_to_c) : dt_inv(Cat)` against the expected type `dt_inv(Animal)`">>,
@ -976,6 +1003,9 @@ failing_contracts() ->
<<?Pos(116,59) <<?Pos(116,59)
"Cannot unify `Cat` and `Animal` in a contravariant context\n" "Cannot unify `Cat` and `Animal` in a contravariant context\n"
"when checking the type of the expression `DT_A_CONTRA_B_CONTRA(f_c_to_c_to_u) : dt_a_contra_b_contra(Cat, Cat)` against the expected type `dt_a_contra_b_contra(Animal, Animal)`">>, "when checking the type of the expression `DT_A_CONTRA_B_CONTRA(f_c_to_c_to_u) : dt_a_contra_b_contra(Cat, Cat)` against the expected type `dt_a_contra_b_contra(Animal, Animal)`">>,
<<?Pos(116,59)
"Cannot unify `Cat` and `Animal` in a contravariant context\n"
"when checking the type of the expression `DT_A_CONTRA_B_CONTRA(f_c_to_c_to_u) : dt_a_contra_b_contra(Cat, Cat)` against the expected type `dt_a_contra_b_contra(Animal, Animal)`">>,
<<?Pos(119,59) <<?Pos(119,59)
"Cannot unify `Cat` and `Animal` in a contravariant context\n" "Cannot unify `Cat` and `Animal` in a contravariant context\n"
"when checking the type of the expression `DT_A_CONTRA_B_CONTRA(f_c_to_a_to_u) : dt_a_contra_b_contra(Cat, Animal)` against the expected type `dt_a_contra_b_contra(Animal, Cat)`">>, "when checking the type of the expression `DT_A_CONTRA_B_CONTRA(f_c_to_a_to_u) : dt_a_contra_b_contra(Cat, Animal)` against the expected type `dt_a_contra_b_contra(Animal, Cat)`">>,
@ -1019,6 +1049,9 @@ failing_contracts() ->
<<?Pos(19,13) <<?Pos(19,13)
"Cannot unify `Cat` and `Animal` in a contravariant context\n" "Cannot unify `Cat` and `Animal` in a contravariant context\n"
"when checking the type of the pattern `o07 : oracle(Animal, Cat)` against the expected type `oracle(Cat, Animal)`">>, "when checking the type of the pattern `o07 : oracle(Animal, Cat)` against the expected type `oracle(Cat, Animal)`">>,
<<?Pos(19,13)
"Cannot unify `Animal` and `Cat` in a covariant context\n"
"when checking the type of the pattern `o07 : oracle(Animal, Cat)` against the expected type `oracle(Cat, Animal)`">>,
<<?Pos(20,13) <<?Pos(20,13)
"Cannot unify `Cat` and `Animal` in a contravariant context\n" "Cannot unify `Cat` and `Animal` in a contravariant context\n"
"when checking the type of the pattern `o08 : oracle(Animal, Cat)` against the expected type `oracle(Cat, Cat)`">>, "when checking the type of the pattern `o08 : oracle(Animal, Cat)` against the expected type `oracle(Cat, Cat)`">>,
@ -1043,6 +1076,9 @@ failing_contracts() ->
<<?Pos(42,13) <<?Pos(42,13)
"Cannot unify `Animal` and `Cat` in a covariant context\n" "Cannot unify `Animal` and `Cat` in a covariant context\n"
"when checking the type of the pattern `q13 : oracle_query(Cat, Cat)` against the expected type `oracle_query(Animal, Animal)`">>, "when checking the type of the pattern `q13 : oracle_query(Cat, Cat)` against the expected type `oracle_query(Animal, Animal)`">>,
<<?Pos(42,13)
"Cannot unify `Animal` and `Cat` in a covariant context\n"
"when checking the type of the pattern `q13 : oracle_query(Cat, Cat)` against the expected type `oracle_query(Animal, Animal)`">>,
<<?Pos(43,13) <<?Pos(43,13)
"Cannot unify `Animal` and `Cat` in a covariant context\n" "Cannot unify `Animal` and `Cat` in a covariant context\n"
"when checking the type of the pattern `q14 : oracle_query(Cat, Cat)` against the expected type `oracle_query(Animal, Cat)`">>, "when checking the type of the pattern `q14 : oracle_query(Cat, Cat)` against the expected type `oracle_query(Animal, Cat)`">>,
@ -1104,19 +1140,19 @@ failing_contracts() ->
]) ])
, ?TYPE_ERROR(polymorphic_aens_resolve, , ?TYPE_ERROR(polymorphic_aens_resolve,
[<<?Pos(4,5) [<<?Pos(4,5)
"Invalid return type of `AENS.resolve`:\n" "Invalid return type of `AENSv2.resolve`:\n"
" `'a`\n" " `'a`\n"
"It must be a `string` or a pubkey type (`address`, `oracle`, etc)">> "It must be a `string` or a pubkey type (`address`, `oracle`, etc)">>
]) ])
, ?TYPE_ERROR(bad_aens_resolve, , ?TYPE_ERROR(bad_aens_resolve,
[<<?Pos(6,5) [<<?Pos(6,5)
"Invalid return type of `AENS.resolve`:\n" "Invalid return type of `AENSv2.resolve`:\n"
" `list(int)`\n" " `list(int)`\n"
"It must be a `string` or a pubkey type (`address`, `oracle`, etc)">> "It must be a `string` or a pubkey type (`address`, `oracle`, etc)">>
]) ])
, ?TYPE_ERROR(bad_aens_resolve_using, , ?TYPE_ERROR(bad_aens_resolve_using,
[<<?Pos(7,5) [<<?Pos(7,5)
"Invalid return type of `AENS.resolve`:\n" "Invalid return type of `AENSv2.resolve`:\n"
" `list(int)`\n" " `list(int)`\n"
"It must be a `string` or a pubkey type (`address`, `oracle`, etc)">> "It must be a `string` or a pubkey type (`address`, `oracle`, etc)">>
]) ])
@ -1266,6 +1302,10 @@ failing_contracts() ->
<<?Pos(3,9) <<?Pos(3,9)
"The name of the compile-time constant cannot have pattern matching">> "The name of the compile-time constant cannot have pattern matching">>
]) ])
, ?TYPE_ERROR(too_many_tvars,
[<<?Pos(2,3)
"Too many type variables (max 256) in definition of `too_many`">>
])
]. ].
validation_test_() -> validation_test_() ->
@ -1313,7 +1353,9 @@ validate(Contract1, Contract2) ->
true -> [debug_mode]; true -> [debug_mode];
false -> [] false -> []
end ++ end ++
[{include, {file_system, [aeso_test_utils:contract_path()]}}]); [ {src_file, lists:concat([Contract2, ".aes"])}
, {include, {file_system, [aeso_test_utils:contract_path()]}}
]);
Error -> print_and_throw(Error) Error -> print_and_throw(Error)
end. end.

View File

@ -0,0 +1,40 @@
-module(aeso_encode_decode_tests).
-compile([export_all, nowarn_export_all]).
-include_lib("eunit/include/eunit.hrl").
-define(EMPTY, "contract C =\n entrypoint f() = true").
encode_decode_test_() ->
[ {lists:flatten(io_lib:format("Testing encode-decode roundtrip for ~p : ~p", [Value, {EType, DType}])),
fun() ->
{ok, SerRes} = aeso_compiler:encode_value(?EMPTY, EType, Value, []),
{ok, Expr} = aeso_compiler:decode_value(?EMPTY, DType, SerRes, []),
Value2 = prettypr:format(aeso_pretty:expr(Expr)),
?assertEqual(Value, Value2)
end} || {Value, EType, DType} <- test_data() ].
test_data() ->
lists:map(fun({V, T}) -> {V, T, T};
({V, T1, T2}) -> {V, T1, T2} end, data()).
data() ->
[ {"42", "int"}
, {"- 42", "int"}
, {"true", "bool"}
, {"ak_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ", "address"}
, {"ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ", "C"}
, {"Some(42)", "option(int)"}
, {"None", "option(int)"}
, {"(true, 42)", "bool * int"}
, {"{[1] = true, [3] = false}", "map(int, bool)"}
, {"()", "unit"}
, {"#000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f", "hash"}
, {"#000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f", "bytes(32)"}
, {"sg_MhibzTP1wWzGCTjtPFr1TiPqRJrrJqw7auvEuF5i3FdoALWqXLBDY6xxRRNUSPHK3EQTnTzF12EyspkxrSMxVHKsZeSMj", "signature"}
, {"sg_MhibzTP1wWzGCTjtPFr1TiPqRJrrJqw7auvEuF5i3FdoALWqXLBDY6xxRRNUSPHK3EQTnTzF12EyspkxrSMxVHKsZeSMj", "bytes(64)", "signature"}
, {"#0102030405060708090a0b0c0d0e0f101718192021222324252627282930313233343536373839401a1b1c1d1e1f202122232425262728293031323334353637", "bytes(64)"}
, {"#0102030405060708090a0b0c0d0e0f101718192021222324252627282930313233343536373839401a1b1c1d1e1f202122232425262728293031323334353637", "signature", "bytes(64)"}
].

View File

@ -53,7 +53,7 @@ simple_contracts_test_() ->
%% associativity %% associativity
[ RightAssoc(Op) || Op <- ["||", "&&", "::", "++"] ], [ RightAssoc(Op) || Op <- ["||", "&&", "::", "++"] ],
[ NonAssoc(Op) || Op <- ["==", "!=", "<", ">", "=<", ">="] ], [ NonAssoc(Op) || Op <- ["==", "!=", "<", ">", "=<", ">="] ],
[ LeftAssoc(Op) || Op <- ["+", "-", "*", "/", "mod"] ], [ LeftAssoc(Op) || Op <- ["+", "-", "*", "/", "mod", "band", "bor", "bxor", "<<", ">>"] ],
%% precedence %% precedence
[ Stronger(Op2, Op1) || [T1 , T2 | _] <- tails(Tiers), Op1 <- T1, Op2 <- T2 ], [ Stronger(Op2, Op1) || [T1 , T2 | _] <- tails(Tiers), Op1 <- T1, Op2 <- T2 ],

View File

@ -39,7 +39,8 @@ all_tokens() ->
%% Symbols %% Symbols
lists:map(Lit, [',', '.', ';', '|', ':', '(', ')', '[', ']', '{', '}']) ++ lists:map(Lit, [',', '.', ';', '|', ':', '(', ')', '[', ']', '{', '}']) ++
%% Operators %% Operators
lists:map(Lit, ['=', '==', '!=', '>', '<', '>=', '=<', '-', '+', '++', '*', '/', mod, ':', '::', '->', '=>', '||', '&&', '!']) ++ lists:map(Lit, ['=', '==', '!=', '>', '<', '>=', '=<', '-', '+', '++', '*', '/', mod,
':', '::', '->', '=>', '||', '&&', '!', 'band', 'bor', 'bxor', 'bnot' ,'<<', '>>']) ++
%% Keywords %% Keywords
lists:map(Lit, [contract, type, 'let', switch]) ++ lists:map(Lit, [contract, type, 'let', switch]) ++
%% Comment token (not an actual token), just for tests %% Comment token (not an actual token), just for tests

View File

@ -6,77 +6,77 @@ main contract AENSTest =
// Name resolution // Name resolution
stateful entrypoint resolve_word(name : string, key : string) : option(address) = stateful entrypoint resolve_word(name : string, key : string) : option(address) =
AENS.resolve(name, key) AENSv2.resolve(name, key)
stateful entrypoint resolve_string(name : string, key : string) : option(string) = stateful entrypoint resolve_string(name : string, key : string) : option(string) =
AENS.resolve(name, key) AENSv2.resolve(name, key)
stateful entrypoint resolve_contract(name : string, key : string) : option(C) = stateful entrypoint resolve_contract(name : string, key : string) : option(C) =
AENS.resolve(name, key) AENSv2.resolve(name, key)
stateful entrypoint resolve_oracle(name : string, key : string) : option(oracle(int, int)) = stateful entrypoint resolve_oracle(name : string, key : string) : option(oracle(int, int)) =
AENS.resolve(name, key) AENSv2.resolve(name, key)
stateful entrypoint resolve_oracle_query(name : string, key : string) : option(oracle_query(int, int)) = stateful entrypoint resolve_oracle_query(name : string, key : string) : option(oracle_query(int, int)) =
AENS.resolve(name, key) AENSv2.resolve(name, key)
// Transactions // Transactions
stateful entrypoint preclaim(addr : address, // Claim on behalf of this account (can be Contract.address) stateful entrypoint preclaim(addr : address, // Claim on behalf of this account (can be Contract.address)
chash : hash) : unit = // Commitment hash chash : hash) : unit = // Commitment hash
AENS.preclaim(addr, chash) AENSv2.preclaim(addr, chash)
stateful entrypoint signedPreclaim(addr : address, // Claim on behalf of this account (can be Contract.address) stateful entrypoint signedPreclaim(addr : address, // Claim on behalf of this account (can be Contract.address)
chash : hash, // Commitment hash chash : hash, // Commitment hash
sign : signature) : unit = // Signed by addr (if not Contract.address) sign : signature) : unit = // Signed by addr (if not Contract.address)
AENS.preclaim(addr, chash, signature = sign) AENSv2.preclaim(addr, chash, signature = sign)
stateful entrypoint claim(addr : address, stateful entrypoint claim(addr : address,
name : string, name : string,
salt : int, salt : int,
name_fee : int) : unit = name_fee : int) : unit =
AENS.claim(addr, name, salt, name_fee) AENSv2.claim(addr, name, salt, name_fee)
stateful entrypoint signedClaim(addr : address, stateful entrypoint signedClaim(addr : address,
name : string, name : string,
salt : int, salt : int,
name_fee : int, name_fee : int,
sign : signature) : unit = sign : signature) : unit =
AENS.claim(addr, name, salt, name_fee, signature = sign) AENSv2.claim(addr, name, salt, name_fee, signature = sign)
stateful entrypoint update(owner : address, stateful entrypoint update(owner : address,
name : string, name : string,
ttl : option(Chain.ttl), ttl : option(Chain.ttl),
client_ttl : option(int), client_ttl : option(int),
pointers : option(map(string, AENS.pointee))) : unit = pointers : option(map(string, AENSv2.pointee))) : unit =
AENS.update(owner, name, ttl, client_ttl, pointers) AENSv2.update(owner, name, ttl, client_ttl, pointers)
stateful entrypoint signedUpdate(owner : address, stateful entrypoint signedUpdate(owner : address,
name : string, name : string,
ttl : option(Chain.ttl), ttl : option(Chain.ttl),
client_ttl : option(int), client_ttl : option(int),
pointers : option(map(string, AENS.pointee)), pointers : option(map(string, AENSv2.pointee)),
sign : signature) : unit = sign : signature) : unit =
AENS.update(owner, name, ttl, client_ttl, pointers, signature = sign) AENSv2.update(owner, name, ttl, client_ttl, pointers, signature = sign)
stateful entrypoint transfer(owner : address, stateful entrypoint transfer(owner : address,
new_owner : address, new_owner : address,
name : string) : unit = name : string) : unit =
AENS.transfer(owner, new_owner, name) AENSv2.transfer(owner, new_owner, name)
stateful entrypoint signedTransfer(owner : address, stateful entrypoint signedTransfer(owner : address,
new_owner : address, new_owner : address,
name : string, name : string,
sign : signature) : unit = sign : signature) : unit =
AENS.transfer(owner, new_owner, name, signature = sign) AENSv2.transfer(owner, new_owner, name, signature = sign)
stateful entrypoint revoke(owner : address, stateful entrypoint revoke(owner : address,
name : string) : unit = name : string) : unit =
AENS.revoke(owner, name) AENSv2.revoke(owner, name)
stateful entrypoint signedRevoke(owner : address, stateful entrypoint signedRevoke(owner : address,
name : string, name : string,
sign : signature) : unit = sign : signature) : unit =
AENS.revoke(owner, name, signature = sign) AENSv2.revoke(owner, name, signature = sign)

View File

@ -1,17 +1,32 @@
contract AENSUpdate = include "Option.aes"
stateful entrypoint update_name(owner : address, name : string) = include "String.aes"
let p1 : AENS.pointee = AENS.AccountPt(Call.caller) include "AENSCompat.aes"
let p2 : AENS.pointee = AENS.OraclePt(Call.caller) contract interface OldAENSContract =
let p3 : AENS.pointee = AENS.ContractPt(Call.caller) entrypoint set : (string, string, AENS.pointee) => unit
let p4 : AENS.pointee = AENS.ChannelPt(Call.caller) entrypoint lookup : (string, string) => AENS.pointee
AENS.update(owner, name, None, None,
Some({ ["account_pubkey"] = p1, ["oracle_pubkey"] = p2, main contract AENSUpdate =
["contract_pubkey"] = p3, ["misc"] = p4 })) stateful entrypoint update_name(owner : address, name : string, b : bytes(2)) =
let p1 : AENSv2.pointee = AENSv2.AccountPt(Call.caller)
let p2 : AENSv2.pointee = AENSv2.OraclePt(Call.caller)
let p3 : AENSv2.pointee = AENSv2.ContractPt(Call.caller)
let p4 : AENSv2.pointee = AENSv2.ChannelPt(Call.caller)
let p5 : AENSv2.pointee = AENSv2.DataPt(String.to_bytes("any something will do"))
let p6 : AENSv2.pointee = AENSv2.DataPt(Int.to_bytes(1345, 4))
AENSv2.update(owner, name, None, None,
Some({ ["account_pubkey"] = p1, ["oracle_pubkey"] = p2,
["contract_pubkey"] = p3, ["misc"] = p4, ["data"] = p5, ["data2"] = p6 }))
stateful entrypoint old_interaction(c : OldAENSContract, owner : address, name : string) =
let p : AENS.pointee = c.lookup(name, "key1")
AENSv2.update(owner, name, None, None, Some({ ["key1"] = AENSCompat.pointee_to_V2(p) }))
switch(AENSv2.lookup(name))
Some(AENSv2.Name(_, _, pt_map)) =>
c.set(name, "key2", Option.force(AENSCompat.pointee_from_V2(pt_map["key1"])))
entrypoint get_ttl(name : string) = entrypoint get_ttl(name : string) =
switch(AENS.lookup(name)) switch(AENSv2.lookup(name))
Some(AENS.Name(_, FixedTTL(ttl), _)) => ttl Some(AENSv2.Name(_, FixedTTL(ttl), _)) => ttl
entrypoint expiry(o : oracle(int, int)) : int = entrypoint expiry(o : oracle(int, int)) : int =
Oracle.expiry(o) Oracle.expiry(o)

View File

@ -3,7 +3,7 @@ contract BadAENSresolve =
type t('a) = option(list('a)) type t('a) = option(list('a))
function fail() : t(int) = function fail() : t(int) =
AENS.resolve("foo.aet", "whatever") AENSv2.resolve("foo.aet", "whatever")
entrypoint main_fun() = () entrypoint main_fun() = ()

View File

@ -1,9 +1,9 @@
contract BadAENSresolve = contract BadAENSresolve =
using AENS using AENSv2
type t('a) = option(list('a)) type t('a) = option(list('a))
function fail() : t(int) = function fail() : t(int) =
resolve("foo.aet", "whatever") resolve("foo.aet", "whatever")
entrypoint main_fun() = () entrypoint main_fun() = ()

View File

@ -0,0 +1,5 @@
// include "String.aes"
contract BytesToX =
entrypoint fail1(b : bytes()) = Bytes.to_fixed_size(b)
entrypoint fail2(b : bytes(4)) = Bytes.to_fixed_size(b)
entrypoint fail3(b : bytes()) = Bytes.to_any_size(b)

View File

@ -0,0 +1,27 @@
include "String.aes"
contract BytesMisc =
entrypoint sizeFixed(b : bytes(4)) : int = Bytes.size(b)
entrypoint sizeAny(b : bytes()) : int = Bytes.size(b)
entrypoint int_to_bytes(i : int) : bytes() = Int.to_bytes(i, 16)
entrypoint test(b3 : bytes(3), b7 : bytes(7), bX : bytes, i : int, s : string) =
let bi = Int.to_bytes(i, 8)
let bs = String.to_bytes(s)
let b10 = Bytes.concat(b3, b7)
let (b4, b6 : bytes(6)) = Bytes.split(b10)
let Some((b8, b2)) = Bytes.split_any(bX, 8)
let bX7 = Bytes.concat(bX, b7)
let Some((b5, bX2)) = Bytes.split_any(bX7, 5)
let Some((b7b, b0)) = Bytes.split_any(bX, Bytes.size(b7))
let Some(b5b : bytes(5)) = Bytes.to_fixed_size(b5)
let (b1 : bytes(1), _) = Bytes.split(b5b)
[bi, bs, b0, Bytes.to_any_size(b1), b2, Bytes.to_any_size(b4), Bytes.to_any_size(b6), b7b, b8, bX2]

View File

@ -6,3 +6,5 @@ contract BytesToX =
String.concat(Bytes.to_str(b), Bytes.to_str(#ffff)) String.concat(Bytes.to_str(b), Bytes.to_str(#ffff))
entrypoint to_str_big(b : bytes(65)) : string = entrypoint to_str_big(b : bytes(65)) : string =
Bytes.to_str(b) Bytes.to_str(b)
entrypoint to_fixed(b : bytes()) : option(bytes(4)) = Bytes.to_fixed_size(b)
entrypoint to_any(b : bytes(4)) = Bytes.to_any_size(b)

15
test/contracts/ceres.aes Normal file
View File

@ -0,0 +1,15 @@
contract C =
entrypoint test() =
let a : int = 23
let b : int = 52
let c = a bor b
let d = c bxor b
let e = d band b
let f = bnot a
let g = f << 2
let h = g >> 2
let i = Int.mulmod(a, b, h)
let j = Crypto.poseidon(i, a)
let k : bytes(32) = Address.to_bytes(Call.origin)
let l = sg_MhibzTP1wWzGCTjtPFr1TiPqRJrrJqw7auvEuF5i3FdoALWqXLBDY6xxRRNUSPHK3EQTnTzF12EyspkxrSMxVHKsZeSMj
(a bor b band c bxor a << bnot b >> a, k, l)

View File

@ -2,7 +2,8 @@
contract ChainTest = contract ChainTest =
record state = { last_bf : address } record state = { last_bf : address
, nw_id : string }
function init() : state = function init() : state =
{last_bf = Contract.address} {last_bf = Contract.address}
@ -11,3 +12,6 @@ contract ChainTest =
function save_coinbase() = function save_coinbase() =
put(state{last_bf = Chain.coinbase}) put(state{last_bf = Chain.coinbase})
function save_network_id() =
put(state{nw_id = Chain.network_id})

View File

@ -0,0 +1,4 @@
include "../dir2/baz.aes"
namespace D =
function g() = E.h()

View File

@ -0,0 +1,3 @@
namespace E =
function h() = 42

View File

@ -20,6 +20,8 @@ contract FunctionArguments =
entrypoint read(a : answer(int)) = entrypoint read(a : answer(int)) =
a.result a.result
entrypoint any_bytes(b : bytes()) = b
entrypoint sjutton(b : bytes(17)) = entrypoint sjutton(b : bytes(17)) =
b b
@ -57,3 +59,5 @@ contract FunctionArguments =
entrypoint chain_ga_meta_tx(tx : Chain.ga_meta_tx) = true entrypoint chain_ga_meta_tx(tx : Chain.ga_meta_tx) = true
entrypoint chain_paying_for_tx(tx : Chain.paying_for_tx) = true entrypoint chain_paying_for_tx(tx : Chain.paying_for_tx) = true
entrypoint chain_base_tx(tx : Chain.base_tx) = true entrypoint chain_base_tx(tx : Chain.base_tx) = true
entrypoint sig(sg : signature) = true

View File

@ -1,7 +1,7 @@
contract PolymorphicAENSresolve = contract PolymorphicAENSresolve =
function fail() : option('a) = function fail() : option('a) =
AENS.resolve("foo.aet", "whatever") AENSv2.resolve("foo.aet", "whatever")
entrypoint main_fun() = () entrypoint main_fun() = ()

View File

@ -0,0 +1,3 @@
include "./dir1/bar.aes"
contract C =
entrypoint f() = D.g()

View File

@ -0,0 +1,60 @@
contract C =
entrypoint too_many(
(_, _, _, _, _, _, _, _, _, _),
(_, _, _, _, _, _, _, _, _, _),
(_, _, _, _, _, _, _, _, _, _),
(_, _, _, _, _, _, _, _, _, _),
(_, _, _, _, _, _, _, _, _, _),
(_, _, _, _, _, _, _, _, _, _),
(_, _, _, _, _, _, _, _, _, _),
(_, _, _, _, _, _, _, _, _, _),
(_, _, _, _, _, _, _, _, _, _),
(_, _, _, _, _, _, _, _, _, _),
(_, _, _, _, _, _, _, _, _, _),
(_, _, _, _, _, _, _, _, _, _),
(_, _, _, _, _, _, _, _, _, _),
(_, _, _, _, _, _, _, _, _, _),
(_, _, _, _, _, _, _, _, _, _),
(_, _, _, _, _, _, _, _, _, _),
(_, _, _, _, _, _, _, _, _, _),
(_, _, _, _, _, _, _, _, _, _),
(_, _, _, _, _, _, _, _, _, _),
(_, _, _, _, _, _, _, _, _, _),
(_, _, _, _, _, _, _, _, _, _),
(_, _, _, _, _, _, _, _, _, _),
(_, _, _, _, _, _, _, _, _, _),
(_, _, _, _, _, _, _, _, _, _),
(_, _, _, _, _, _, _, _, _, _),
(_, _, _, _, _, _, _, _, _, _)) = 0
entrypoint not_too_many(
(_, _, _, _, _, _, _, _, _, _),
(_, _, _, _, _, _, _, _, _, _),
(_, _, _, _, _, _, _, _, _, _),
(_, _, _, _, _, _, _, _, _, _),
(_, _, _, _, _, _, _, _, _, _),
(_, _, _, _, _, _, _, _, _, _),
(_, _, _, _, _, _, _, _, _, _),
(_, _, _, _, _, _, _, _, _, _),
(_, _, _, _, _, _, _, _, _, _),
(_, _, _, _, _, _, _, _, _, _),
(_, _, _, _, _, _, _, _, _, _),
(_, _, _, _, _, _, _, _, _, _),
(_, _, _, _, _, _, _, _, _, _),
(_, _, _, _, _, _, _, _, _, _),
(_, _, _, _, _, _, _, _, _, _),
(_, _, _, _, _, _, _, _, _, _),
(_, _, _, _, _, _, _, _, _, _),
(_, _, _, _, _, _, _, _, _, _),
(_, _, _, _, _, _, _, _, _, _),
(_, _, _, _, _, _, _, _, _, _),
(_, _, _, _, _, _, _, _, _, _),
(_, _, _, _, _, _, _, _, _, _),
(_, _, _, _, _, _, _, _, _, _),
(_, _, _, _, _, _, _, _, _, _),
(_, _, _, _, _, _, _, _, _, _),
(_, _, _, _, _, _)) = 0

View File

@ -2,10 +2,10 @@
// Named argument builtins are: // Named argument builtins are:
// Oracle.register // Oracle.register
// Oracle.respond // Oracle.respond
// AENS.preclaim // AENSv2.preclaim
// AENS.claim // AENSv2.claim
// AENS.transfer // AENSv2.transfer
// AENS.revoke // AENSv2.revoke
// Oracle.extend // Oracle.extend
include "String.aes" include "String.aes"
contract UnappliedBuiltins = contract UnappliedBuiltins =
@ -28,7 +28,7 @@ contract UnappliedBuiltins =
function oracle_get_answer() = Oracle.get_answer : (o, _) => _ function oracle_get_answer() = Oracle.get_answer : (o, _) => _
function oracle_check() = Oracle.check : o => _ function oracle_check() = Oracle.check : o => _
function oracle_check_query() = Oracle.check_query : (o, _) => _ function oracle_check_query() = Oracle.check_query : (o, _) => _
function aens_resolve() = AENS.resolve : (_, _) => option(string) function aens_resolve() = AENSv2.resolve : (_, _) => option(string)
function map_lookup() = Map.lookup : (_, m) => _ function map_lookup() = Map.lookup : (_, m) => _
function map_lookup_default() = Map.lookup_default : (_, m, _) => _ function map_lookup_default() = Map.lookup_default : (_, m, _) => _
function map_member() = Map.member : (_, m) => _ function map_member() = Map.member : (_, m) => _
@ -36,7 +36,7 @@ contract UnappliedBuiltins =
function map_delete() = Map.delete : (_, m) => _ function map_delete() = Map.delete : (_, m) => _
function map_from_list() = Map.from_list : _ => m function map_from_list() = Map.from_list : _ => m
function map_to_list() = Map.to_list : m => _ function map_to_list() = Map.to_list : m => _
function crypto_verify_sig() = Crypto.verify_sig function crypto_verify_sig() = Crypto.verify_sig : (bytes(), _, _) => _
function crypto_verify_sig_secp256k1() = Crypto.verify_sig_secp256k1 function crypto_verify_sig_secp256k1() = Crypto.verify_sig_secp256k1
function crypto_ecverify_secp256k1() = Crypto.ecverify_secp256k1 function crypto_ecverify_secp256k1() = Crypto.ecverify_secp256k1
function crypto_ecrecover_secp256k1() = Crypto.ecrecover_secp256k1 function crypto_ecrecover_secp256k1() = Crypto.ecrecover_secp256k1

View File

@ -0,0 +1,5 @@
contract Test =
type option_int = option(int)
record option_point = {x: int, y: option_int}
entrypoint test_option_record(a: option_point) = a