Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8e191b0c88 | |||
| f70fc56df8 | |||
| 4ae24722f4 | |||
| 55a97852ed | |||
| 1380142082 | |||
| 1754763e23 | |||
| 83abfae32b | |||
| 4ca90feea0 | |||
| 09638daa90 | |||
| d59023a9f4 | |||
| 34b52739fd | |||
| 1c83287d45 | |||
| da92ddbd5d | |||
| c1c169273c |
+29
-1
@@ -4,11 +4,39 @@ All notable changes to this project will be documented in this file.
|
|||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
## [Unreleased]
|
## [CERES 8.0.0]
|
||||||
### Added
|
### 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(string)`. 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.
|
||||||
### Changed
|
### Changed
|
||||||
### Removed
|
### Removed
|
||||||
|
- `Bitwise.aes` standard library is removed - the builtin operations are superior.
|
||||||
|
|
||||||
|
## [Unreleased]
|
||||||
|
### Added
|
||||||
|
- Options to enable/disable certain optimizations.
|
||||||
|
- The ability to call a different instance of the current contract
|
||||||
|
```
|
||||||
|
contract Main =
|
||||||
|
entrypoint spend(x : int) : int = x
|
||||||
|
entrypoint f(c : Main) : int = c.spend(10)
|
||||||
|
```
|
||||||
|
- Return a mapping from variables to FATE registers in the compilation output.
|
||||||
|
### Changed
|
||||||
|
- Type definitions serialised to ACI as `typedefs` field instead of `type_defs` to increase compatibility.
|
||||||
|
### Removed
|
||||||
### Fixed
|
### Fixed
|
||||||
|
- Typechecker crashes if Chain.create or Chain.clone are used without arguments.
|
||||||
|
|
||||||
## [7.0.1]
|
## [7.0.1]
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ The following points should be considered before creating a new PR to the Sophia
|
|||||||
- If a PR introduces a new feature that is relevant to the users of the language, the [Sophia Features Documentation](docs/sophia_features.md) should be updated to describe the new feature.
|
- If a PR introduces a new feature that is relevant to the users of the language, the [Sophia Features Documentation](docs/sophia_features.md) should be updated to describe the new feature.
|
||||||
- If a PR introduces new syntax (e.g. changes in [aeso_syntax.erl](src/aeso_syntax.erl), [aeso_scan.erl](src/aeso_scan.erl), or [aeso_parser.erl](src/aeso_parser.erl)), the [Sophia Syntax Documentation](docs/sophia_syntax.md) should be updated to include the new syntax.
|
- If a PR introduces new syntax (e.g. changes in [aeso_syntax.erl](src/aeso_syntax.erl), [aeso_scan.erl](src/aeso_scan.erl), or [aeso_parser.erl](src/aeso_parser.erl)), the [Sophia Syntax Documentation](docs/sophia_syntax.md) should be updated to include the new syntax.
|
||||||
- If a PR introduces a new library, the public interface of the new library should be fully documented in the [Sophia Standard Library Documentation](docs/sophia_stdlib.md).
|
- If a PR introduces a new library, the public interface of the new library should be fully documented in the [Sophia Standard Library Documentation](docs/sophia_stdlib.md).
|
||||||
|
- If a PR introduces a new compiler option, the new option should be documented in the file
|
||||||
|
[aeso_compiler.md](docs/aeso_compiler.md).
|
||||||
|
|
||||||
### Tests
|
### Tests
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -67,7 +67,7 @@ generates the following JSON structure representing the contract interface:
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"type_defs": [
|
"typedefs": [
|
||||||
{
|
{
|
||||||
"name": "answers",
|
"name": "answers",
|
||||||
"typedef": {
|
"typedef": {
|
||||||
@@ -138,7 +138,7 @@ be included inside another contract.
|
|||||||
state =>
|
state =>
|
||||||
#{record =>
|
#{record =>
|
||||||
[#{name => <<"a">>,type => <<"Answers.answers">>}]},
|
[#{name => <<"a">>,type => <<"Answers.answers">>}]},
|
||||||
type_defs =>
|
typedefs =>
|
||||||
[#{name => <<"answers">>,
|
[#{name => <<"answers">>,
|
||||||
typedef => #{<<"map">> => [<<"string">>,<<"int">>]},
|
typedef => #{<<"map">> => [<<"string">>,<<"int">>]},
|
||||||
vars => []}]}}]}
|
vars => []}]}}]}
|
||||||
|
|||||||
@@ -51,6 +51,36 @@ The **pp_** options all print to standard output the following:
|
|||||||
|
|
||||||
`pp_assembler` - print the generated assembler code
|
`pp_assembler` - print the generated assembler code
|
||||||
|
|
||||||
|
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:
|
||||||
|
|
||||||
|
By default all optimizations are turned on, to disable an optimization, it should be
|
||||||
|
explicitly set to false and passed as a compiler option.
|
||||||
|
|
||||||
|
List of optimizations:
|
||||||
|
|
||||||
|
- optimize_inliner
|
||||||
|
- optimize_inline_local_functions
|
||||||
|
- optimize_bind_subexpressions
|
||||||
|
- optimize_let_floating
|
||||||
|
- optimize_simplifier
|
||||||
|
- optimize_drop_unused_lets
|
||||||
|
- optimize_push_consume
|
||||||
|
- optimize_one_shot_var
|
||||||
|
- optimize_write_to_dead_var
|
||||||
|
- optimize_inline_switch_target
|
||||||
|
- optimize_swap_push
|
||||||
|
- optimize_swap_pop
|
||||||
|
- optimize_swap_write
|
||||||
|
- optimize_constant_propagation
|
||||||
|
- optimize_prune_impossible_branches
|
||||||
|
- optimize_single_successful_branch
|
||||||
|
- optimize_inline_store
|
||||||
|
- optimize_float_switch_bod
|
||||||
|
|
||||||
#### check_call(ContractString, Options) -> CheckRet
|
#### check_call(ContractString, Options) -> CheckRet
|
||||||
|
|
||||||
Types
|
Types
|
||||||
|
|||||||
@@ -564,6 +564,14 @@ 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`)
|
||||||
|
|
||||||
## Bit fields
|
## Bit fields
|
||||||
|
|
||||||
Sophia integers do not support bit arithmetic. Instead there is a separate
|
Sophia integers do not support bit arithmetic. Instead there is a separate
|
||||||
|
|||||||
+104
-95
@@ -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)
|
||||||
@@ -90,13 +92,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 +112,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(string)
|
||||||
|
```
|
||||||
|
|
||||||
|
Note: on-chain there is a maximum length enforced for `DataPt`, it is 1024 bytes.
|
||||||
|
Sophia itself does _not_ enforce 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,41 +157,53 @@ 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 over
|
||||||
`network id` + `owner address` + `Contract.address` (concatenated as byte arrays).
|
`network id` + `owner address` + `Contract.address` (concatenated as byte arrays).
|
||||||
|
|
||||||
|
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. over `network id` + `owner address` + `string "AENS"` +
|
||||||
|
`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 over
|
||||||
`network id` + `owner address` + `name_hash` + `Contract.address`
|
`network id` + `owner address` + `name_hash` + `Contract.address` (concatenated
|
||||||
(concatenated as byte arrays)
|
as byte arrays) using the private key of the `owner` account 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. over `network id` + `owner address` + `string "AENS"` +
|
||||||
|
`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.
|
||||||
@@ -173,10 +213,16 @@ The [signature](./sophia_features.md#delegation-signature) should be over
|
|||||||
(concatenated as byte arrays)
|
(concatenated as byte arrays)
|
||||||
using the private key of the `owner` account 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. over `network id` + `owner address` + `string "AENS"` +
|
||||||
|
`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.
|
||||||
@@ -186,17 +232,24 @@ The [signature](./sophia_features.md#delegation-signature) should be over
|
|||||||
(concatenated as byte arrays)
|
(concatenated as byte arrays)
|
||||||
using the private key of the `owner` account 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. over `network id` + `owner address` + `string "AENS"` +
|
||||||
|
`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 : 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).
|
||||||
|
|
||||||
### Auth
|
### Auth
|
||||||
|
|
||||||
@@ -236,7 +289,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
|
||||||
@@ -381,6 +437,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,6 +531,13 @@ 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).
|
||||||
|
|
||||||
|
#### to_bytes
|
||||||
|
```
|
||||||
|
Address.to_bytes(a : address) : bytes(32)
|
||||||
|
```
|
||||||
|
|
||||||
|
The binary representation of the address.
|
||||||
|
|
||||||
|
|
||||||
##### coinbase
|
##### coinbase
|
||||||
```
|
```
|
||||||
@@ -538,6 +607,13 @@ 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.
|
||||||
|
|
||||||
|
#### 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.
|
||||||
|
|
||||||
The type `'c` must be instantiated with a contract.
|
The type `'c` must be instantiated with a contract.
|
||||||
|
|
||||||
@@ -914,88 +990,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
|
||||||
|
|||||||
@@ -254,8 +254,8 @@ Path ::= Id // Record field
|
|||||||
|
|
||||||
BinOp ::= '||' | '&&' | '<' | '>' | '=<' | '>=' | '==' | '!='
|
BinOp ::= '||' | '&&' | '<' | '>' | '=<' | '>=' | '==' | '!='
|
||||||
| '::' | '++' | '+' | '-' | '*' | '/' | 'mod' | '^'
|
| '::' | '++' | '+' | '-' | '*' | '/' | 'mod' | '^'
|
||||||
| '|>'
|
| 'band' | 'bor' | 'bxor' | '<<' | '>>' | '|>'
|
||||||
UnOp ::= '-' | '!'
|
UnOp ::= '-' | '!' | 'bnot'
|
||||||
```
|
```
|
||||||
|
|
||||||
## Operators types
|
## Operators types
|
||||||
@@ -264,6 +264,7 @@ UnOp ::= '-' | '!'
|
|||||||
| --- | ---
|
| --- | ---
|
||||||
| `-` `+` `*` `/` `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
|
||||||
@@ -274,13 +275,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
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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)
|
|
||||||
+2
-2
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
{erl_opts, [debug_info]}.
|
{erl_opts, [debug_info]}.
|
||||||
|
|
||||||
{deps, [ {aebytecode, {git, "https://github.com/aeternity/aebytecode.git", {tag, "v3.1.1"}}}
|
{deps, [ {aebytecode, {git, "https://github.com/aeternity/aebytecode.git", {tag, "v3.2.0"}}}
|
||||||
, {getopt, "1.0.1"}
|
, {getopt, "1.0.1"}
|
||||||
, {eblake2, "1.0.0"}
|
, {eblake2, "1.0.0"}
|
||||||
, {jsx, {git, "https://github.com/talentdeficit/jsx.git", {tag, "2.8.0"}}}
|
, {jsx, {git, "https://github.com/talentdeficit/jsx.git", {tag, "2.8.0"}}}
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
{base_plt_apps, [erts, kernel, stdlib, crypto, mnesia]}
|
{base_plt_apps, [erts, kernel, stdlib, crypto, mnesia]}
|
||||||
]}.
|
]}.
|
||||||
|
|
||||||
{relx, [{release, {aesophia, "7.0.1"},
|
{relx, [{release, {aesophia, "8.0.0"},
|
||||||
[aesophia, aebytecode, getopt]},
|
[aesophia, aebytecode, getopt]},
|
||||||
|
|
||||||
{dev_mode, true},
|
{dev_mode, true},
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
{"1.2.0",
|
{"1.2.0",
|
||||||
[{<<"aebytecode">>,
|
[{<<"aebytecode">>,
|
||||||
{git,"https://github.com/aeternity/aebytecode.git",
|
{git,"https://github.com/aeternity/aebytecode.git",
|
||||||
{ref,"8269dbd71e9011921c60141636f1baa270a0e784"}},
|
{ref,"2a0a397afad6b45da52572170f718194018bf33c"}},
|
||||||
0},
|
0},
|
||||||
{<<"aeserialization">>,
|
{<<"aeserialization">>,
|
||||||
{git,"https://github.com/aeternity/aeserialization.git",
|
{git,"https://github.com/aeternity/aeserialization.git",
|
||||||
|
|||||||
+4
-4
@@ -91,7 +91,7 @@ encode_contract(Contract = {Head, _, {con, _, Name}, _, _}) when ?IS_CONTRACT_HE
|
|||||||
{Es, Tdefs1} = lists:partition(FilterT(<<"event">>), Tdefs0),
|
{Es, Tdefs1} = lists:partition(FilterT(<<"event">>), Tdefs0),
|
||||||
{Ss, Tdefs} = lists:partition(FilterT(<<"state">>), Tdefs1),
|
{Ss, Tdefs} = lists:partition(FilterT(<<"state">>), Tdefs1),
|
||||||
|
|
||||||
C1 = C0#{type_defs => Tdefs},
|
C1 = C0#{typedefs => Tdefs},
|
||||||
|
|
||||||
C2 = case Es of
|
C2 = case Es of
|
||||||
[] -> C1;
|
[] -> C1;
|
||||||
@@ -111,7 +111,7 @@ encode_contract(Contract = {Head, _, {con, _, Name}, _, _}) when ?IS_CONTRACT_HE
|
|||||||
encode_contract(Namespace = {namespace, _, {con, _, Name}, _}) ->
|
encode_contract(Namespace = {namespace, _, {con, _, Name}, _}) ->
|
||||||
Tdefs = [ encode_typedef(T) || T <- sort_decls(contract_types(Namespace)) ],
|
Tdefs = [ encode_typedef(T) || T <- sort_decls(contract_types(Namespace)) ],
|
||||||
#{namespace => #{name => encode_name(Name),
|
#{namespace => #{name => encode_name(Name),
|
||||||
type_defs => Tdefs}}.
|
typedefs => Tdefs}}.
|
||||||
|
|
||||||
%% Encode a function definition. Currently we are only interested in
|
%% Encode a function definition. Currently we are only interested in
|
||||||
%% the interface and type.
|
%% the interface and type.
|
||||||
@@ -234,7 +234,7 @@ do_render_aci_json(Json) ->
|
|||||||
decode_contract(#{contract := #{name := Name,
|
decode_contract(#{contract := #{name := Name,
|
||||||
kind := Kind,
|
kind := Kind,
|
||||||
payable := Payable,
|
payable := Payable,
|
||||||
type_defs := Ts0,
|
typedefs := Ts0,
|
||||||
functions := Fs} = C}) ->
|
functions := Fs} = C}) ->
|
||||||
MkTDef = fun(N, T) -> #{name => N, vars => [], typedef => T} end,
|
MkTDef = fun(N, T) -> #{name => N, vars => [], typedef => T} end,
|
||||||
Ts = [ MkTDef(<<"state">>, maps:get(state, C)) || maps:is_key(state, C) ] ++
|
Ts = [ MkTDef(<<"state">>, maps:get(state, C)) || maps:is_key(state, C) ] ++
|
||||||
@@ -246,7 +246,7 @@ decode_contract(#{contract := #{name := Name,
|
|||||||
end,
|
end,
|
||||||
io_lib:format("~s", [Name])," =\n",
|
io_lib:format("~s", [Name])," =\n",
|
||||||
decode_tdefs(Ts), decode_funcs(Fs)];
|
decode_tdefs(Ts), decode_funcs(Fs)];
|
||||||
decode_contract(#{namespace := #{name := Name, type_defs := Ts}}) when Ts /= [] ->
|
decode_contract(#{namespace := #{name := Name, typedefs := Ts}}) when Ts /= [] ->
|
||||||
["namespace ", io_lib:format("~s", [Name])," =\n",
|
["namespace ", io_lib:format("~s", [Name])," =\n",
|
||||||
decode_tdefs(Ts)];
|
decode_tdefs(Ts)];
|
||||||
decode_contract(_) -> [].
|
decode_contract(_) -> [].
|
||||||
|
|||||||
+176
-85
@@ -270,15 +270,24 @@ bind_state(Env) ->
|
|||||||
false -> Env1
|
false -> Env1
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec bind_field(name(), field_info(), env()) -> env().
|
-spec bind_field_append(name(), field_info(), env()) -> env().
|
||||||
bind_field(X, Info, Env = #env{ fields = Fields }) ->
|
bind_field_append(X, Info, Env = #env{ fields = Fields }) ->
|
||||||
Fields1 = maps:update_with(X, fun(Infos) -> [Info | Infos] end, [Info], Fields),
|
Fields1 = maps:update_with(X, fun(Infos) -> [Info | Infos] end, [Info], Fields),
|
||||||
Env#env{ fields = Fields1 }.
|
Env#env{ fields = Fields1 }.
|
||||||
|
|
||||||
-spec bind_fields([{name(), field_info()}], env()) -> env().
|
-spec bind_field_update(name(), field_info(), env()) -> env().
|
||||||
bind_fields([], Env) -> Env;
|
bind_field_update(X, Info, Env = #env{ fields = Fields }) ->
|
||||||
bind_fields([{Id, Info} | Rest], Env) ->
|
Fields1 = maps:update_with(X, fun([_ | Infos]) -> [Info | Infos]; ([]) -> [Info] end, [Info], Fields),
|
||||||
bind_fields(Rest, bind_field(Id, Info, Env)).
|
Env#env{ fields = Fields1 }.
|
||||||
|
|
||||||
|
-spec bind_fields([{name(), field_info()}], typed | untyped, env()) -> env().
|
||||||
|
bind_fields([], _Typing, Env) -> Env;
|
||||||
|
bind_fields([{Id, Info} | Rest], Typing, Env) ->
|
||||||
|
NewEnv = case Typing of
|
||||||
|
untyped -> bind_field_append(Id, Info, Env);
|
||||||
|
typed -> bind_field_update(Id, Info, Env)
|
||||||
|
end,
|
||||||
|
bind_fields(Rest, Typing, NewEnv).
|
||||||
|
|
||||||
%% Contract entrypoints take three named arguments
|
%% Contract entrypoints take three named arguments
|
||||||
%% gas : int = Call.gas_left()
|
%% gas : int = Call.gas_left()
|
||||||
@@ -296,26 +305,27 @@ contract_call_type({fun_t, Ann, [], Args, Ret}) ->
|
|||||||
Named("protected", Typed({bool, Ann, false}, Id("bool")))],
|
Named("protected", Typed({bool, Ann, false}, Id("bool")))],
|
||||||
Args, {if_t, Ann, Id("protected"), {app_t, Ann, {id, Ann, "option"}, [Ret]}, Ret}}.
|
Args, {if_t, Ann, Id("protected"), {app_t, Ann, {id, Ann, "option"}, [Ret]}, Ret}}.
|
||||||
|
|
||||||
-spec bind_contract(aeso_syntax:decl(), env()) -> env().
|
-spec bind_contract(typed | untyped, aeso_syntax:decl(), env()) -> env().
|
||||||
bind_contract({Contract, Ann, Id, _Impls, Contents}, Env)
|
bind_contract(Typing, {Contract, Ann, Id, _Impls, Contents}, Env)
|
||||||
when ?IS_CONTRACT_HEAD(Contract) ->
|
when ?IS_CONTRACT_HEAD(Contract) ->
|
||||||
Key = name(Id),
|
Key = name(Id),
|
||||||
Sys = [{origin, system}],
|
Sys = [{origin, system}],
|
||||||
Fields =
|
TypeOrFresh = fun({typed, _, _, Type}) -> Type; (_) -> fresh_uvar(Sys) end,
|
||||||
|
Fields =
|
||||||
[ {field_t, AnnF, Entrypoint, contract_call_type(Type)}
|
[ {field_t, AnnF, Entrypoint, contract_call_type(Type)}
|
||||||
|| {fun_decl, AnnF, Entrypoint, Type} <- 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, [], [ArgT || {typed, _, _, ArgT} <- Args], RetT})
|
{fun_t, AnnF, [], [TypeOrFresh(Arg) || Arg <- Args], TypeOrFresh(Ret)})
|
||||||
}
|
}
|
||||||
|| {letfun, AnnF, Entrypoint = {id, _, Name}, Args, _Type, [{guarded, _, [], {typed, _, _, RetT}}]} <- Contents,
|
|| {letfun, AnnF, Entrypoint = {id, _, Name}, Args, _Type, [{guarded, _, [], Ret}]} <- Contents,
|
||||||
Name =/= "init"
|
Name =/= "init"
|
||||||
] ++
|
] ++
|
||||||
%% Predefined fields
|
%% Predefined fields
|
||||||
[ {field_t, Sys, {id, Sys, "address"}, {id, Sys, "address"}} ] ++
|
[ {field_t, Sys, {id, Sys, "address"}, {id, Sys, "address"}} ] ++
|
||||||
[ {field_t, Sys, {id, Sys, ?CONSTRUCTOR_MOCK_NAME},
|
[ {field_t, Sys, {id, Sys, ?CONSTRUCTOR_MOCK_NAME},
|
||||||
contract_call_type(
|
contract_call_type(
|
||||||
case [ [ArgT || {typed, _, _, ArgT} <- Args]
|
case [ [TypeOrFresh(Arg) || Arg <- Args]
|
||||||
|| {letfun, AnnF, {id, _, "init"}, Args, _, _} <- Contents,
|
|| {letfun, AnnF, {id, _, "init"}, Args, _, _} <- Contents,
|
||||||
aeso_syntax:get_ann(entrypoint, AnnF, false)]
|
aeso_syntax:get_ann(entrypoint, AnnF, false)]
|
||||||
++ [ Args
|
++ [ Args
|
||||||
@@ -337,7 +347,7 @@ bind_contract({Contract, Ann, Id, _Impls, Contents}, Env)
|
|||||||
record_t = Id }}
|
record_t = Id }}
|
||||||
|| {field_t, _, {id, FieldAnn, Entrypoint}, Type} <- Fields ],
|
|| {field_t, _, {id, FieldAnn, Entrypoint}, Type} <- Fields ],
|
||||||
bind_type(Key, Ann, {[], {contract_t, Fields}},
|
bind_type(Key, Ann, {[], {contract_t, Fields}},
|
||||||
bind_fields(FieldInfo, Env)).
|
bind_fields(FieldInfo, Typing, Env)).
|
||||||
|
|
||||||
%% What scopes could a given name come from?
|
%% What scopes could a given name come from?
|
||||||
-spec possible_scopes(env(), qname()) -> [qname()].
|
-spec possible_scopes(env(), qname()) -> [qname()].
|
||||||
@@ -447,6 +457,9 @@ lookup_env1(#env{ namespace = Current, used_namespaces = UsedNamespaces, scopes
|
|||||||
end
|
end
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
fun_arity({fun_t, _, _, Args, _}) -> length(Args);
|
||||||
|
fun_arity(_) -> none.
|
||||||
|
|
||||||
-spec lookup_record_field(env(), name()) -> [field_info()].
|
-spec lookup_record_field(env(), name()) -> [field_info()].
|
||||||
lookup_record_field(Env, FieldName) ->
|
lookup_record_field(Env, FieldName) ->
|
||||||
maps:get(FieldName, Env#env.fields, []).
|
maps:get(FieldName, Env#env.fields, []).
|
||||||
@@ -457,6 +470,11 @@ lookup_record_field(Env, FieldName, Kind) ->
|
|||||||
[ Fld || Fld = #field_info{ kind = K } <- lookup_record_field(Env, FieldName),
|
[ Fld || Fld = #field_info{ kind = K } <- lookup_record_field(Env, FieldName),
|
||||||
Kind == project orelse K /= contract ].
|
Kind == project orelse K /= contract ].
|
||||||
|
|
||||||
|
lookup_record_field_arity(Env, FieldName, Arity, Kind) ->
|
||||||
|
Fields = lookup_record_field(Env, FieldName, Kind),
|
||||||
|
[ Fld || Fld = #field_info{ field_t = FldType } <- Fields,
|
||||||
|
fun_arity(dereference_deep(FldType)) == Arity ].
|
||||||
|
|
||||||
%% -- Name manipulation ------------------------------------------------------
|
%% -- Name manipulation ------------------------------------------------------
|
||||||
|
|
||||||
-spec qname(type_id()) -> qname().
|
-spec qname(type_id()) -> qname().
|
||||||
@@ -522,6 +540,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]},
|
||||||
@@ -657,14 +677,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)},
|
||||||
@@ -674,6 +687,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(String, 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))},
|
||||||
@@ -693,7 +726,8 @@ global_env() ->
|
|||||||
{"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
|
||||||
@@ -778,14 +812,16 @@ global_env() ->
|
|||||||
]) },
|
]) },
|
||||||
|
|
||||||
%% Conversion
|
%% Conversion
|
||||||
IntScope = #scope{ funs = MkDefs([{"to_str", Fun1(Int, String)}]) },
|
IntScope = #scope{ funs = MkDefs([{"to_str", Fun1(Int, String)},
|
||||||
|
{"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
|
||||||
@@ -793,6 +829,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
|
||||||
@@ -837,6 +874,7 @@ infer(Contracts, Options) ->
|
|||||||
ets_new(type_vars, [set]),
|
ets_new(type_vars, [set]),
|
||||||
ets_new(warnings, [bag]),
|
ets_new(warnings, [bag]),
|
||||||
ets_new(type_vars_variance, [set]),
|
ets_new(type_vars_variance, [set]),
|
||||||
|
ets_new(functions_to_implement, [set]),
|
||||||
%% Set the variance for builtin types
|
%% Set the variance for builtin types
|
||||||
ets_insert(type_vars_variance, {"list", [covariant]}),
|
ets_insert(type_vars_variance, {"list", [covariant]}),
|
||||||
ets_insert(type_vars_variance, {"option", [covariant]}),
|
ets_insert(type_vars_variance, {"option", [covariant]}),
|
||||||
@@ -871,7 +909,7 @@ infer(Contracts, Options) ->
|
|||||||
-spec infer1(env(), [aeso_syntax:decl()], [aeso_syntax:decl()], list(option())) ->
|
-spec infer1(env(), [aeso_syntax:decl()], [aeso_syntax:decl()], list(option())) ->
|
||||||
{env(), [aeso_syntax:decl()]}.
|
{env(), [aeso_syntax:decl()]}.
|
||||||
infer1(Env, [], Acc, _Options) -> {Env, lists:reverse(Acc)};
|
infer1(Env, [], Acc, _Options) -> {Env, lists:reverse(Acc)};
|
||||||
infer1(Env0, [{Contract, Ann, ConName, Impls, Code} | Rest], Acc, Options)
|
infer1(Env0, [Contract0 = {Contract, Ann, ConName, Impls, Code} | Rest], Acc, Options)
|
||||||
when ?IS_CONTRACT_HEAD(Contract) ->
|
when ?IS_CONTRACT_HEAD(Contract) ->
|
||||||
%% do type inference on each contract independently.
|
%% do type inference on each contract independently.
|
||||||
Env = Env0#env{ contract_parents = maps:put(name(ConName),
|
Env = Env0#env{ contract_parents = maps:put(name(ConName),
|
||||||
@@ -887,12 +925,15 @@ infer1(Env0, [{Contract, Ann, ConName, Impls, Code} | Rest], Acc, Options)
|
|||||||
contract -> ets_insert(defined_contracts, {qname(ConName)});
|
contract -> ets_insert(defined_contracts, {qname(ConName)});
|
||||||
contract_interface -> ok
|
contract_interface -> ok
|
||||||
end,
|
end,
|
||||||
{Env1, Code1} = infer_contract_top(push_scope(contract, ConName, Env), What, Code, Options),
|
populate_functions_to_implement(Env, ConName, Impls, Acc),
|
||||||
|
Env1 = bind_contract(untyped, Contract0, Env),
|
||||||
|
{Env2, Code1} = infer_contract_top(push_scope(contract, ConName, Env1), What, Code, Options),
|
||||||
|
report_unimplemented_functions(Env1, ConName),
|
||||||
Contract1 = {Contract, Ann, ConName, Impls, Code1},
|
Contract1 = {Contract, Ann, ConName, Impls, Code1},
|
||||||
check_implemented_interfaces(Env1, Contract1, Acc),
|
Env3 = pop_scope(Env2),
|
||||||
Env2 = pop_scope(Env1),
|
%% Rebinding because the qualifications of types are added during type inference. Could we do better?
|
||||||
Env3 = bind_contract(Contract1, Env2),
|
Env4 = bind_contract(typed, Contract1, Env3),
|
||||||
infer1(Env3, Rest, [Contract1 | Acc], Options);
|
infer1(Env4, Rest, [Contract1 | Acc], Options);
|
||||||
infer1(Env, [{namespace, Ann, Name, Code} | Rest], Acc, Options) ->
|
infer1(Env, [{namespace, Ann, Name, Code} | Rest], Acc, Options) ->
|
||||||
when_warning(warn_unused_includes,
|
when_warning(warn_unused_includes,
|
||||||
fun() ->
|
fun() ->
|
||||||
@@ -909,54 +950,52 @@ infer1(Env, [{pragma, _, _} | Rest], Acc, Options) ->
|
|||||||
%% Pragmas are checked in check_modifiers
|
%% Pragmas are checked in check_modifiers
|
||||||
infer1(Env, Rest, Acc, Options).
|
infer1(Env, Rest, Acc, Options).
|
||||||
|
|
||||||
check_implemented_interfaces(Env, {_Contract, _Ann, ConName, Impls, Code}, DefinedContracts) ->
|
%% Report all functions that were not implemented by the contract ContractName.
|
||||||
|
-spec report_unimplemented_functions(env(), ContractName) -> ok | no_return() when
|
||||||
|
ContractName :: aeso_syntax:con().
|
||||||
|
report_unimplemented_functions(Env, ContractName) ->
|
||||||
create_type_errors(),
|
create_type_errors(),
|
||||||
AllInterfaces = [{name(IName), I} || I = {contract_interface, _, IName, _, _} <- DefinedContracts],
|
[ type_error({unimplemented_interface_function, ContractName, name(I), FunName})
|
||||||
ImplsNames = lists:map(fun name/1, Impls),
|
|| {FunName, I, _} <- ets_tab2list(functions_to_implement) ],
|
||||||
|
|
||||||
%% All implemented intrefaces should already be defined
|
|
||||||
lists:foreach(fun(Impl) -> case proplists:get_value(name(Impl), AllInterfaces) of
|
|
||||||
undefined -> type_error({referencing_undefined_interface, Impl});
|
|
||||||
_ -> ok
|
|
||||||
end
|
|
||||||
end, Impls),
|
|
||||||
|
|
||||||
ImplementedInterfaces = [I || I <- [proplists:get_value(Name, AllInterfaces) || Name <- ImplsNames],
|
|
||||||
I /= undefined],
|
|
||||||
Funs = [ Fun || Fun <- Code,
|
|
||||||
element(1, Fun) == letfun orelse element(1, Fun) == fun_decl ],
|
|
||||||
check_implemented_interfaces1(Env, ImplementedInterfaces, ConName, Funs, AllInterfaces),
|
|
||||||
destroy_and_report_type_errors(Env).
|
destroy_and_report_type_errors(Env).
|
||||||
|
|
||||||
%% Recursively check that all directly and indirectly referenced interfaces are implemented
|
%% Return a list of all function declarations to be implemented, given the list
|
||||||
check_implemented_interfaces1(_, [], _, _, _) ->
|
%% of interfaces to be implemented Impls and all the previously defined
|
||||||
ok;
|
%% contracts DefinedContracts>
|
||||||
check_implemented_interfaces1(Env, [{contract_interface, _, IName, _, Decls} | Interfaces],
|
-spec functions_to_implement(Impls, DefinedContracts) -> [{InterfaceCon, FunDecl}] when
|
||||||
ConId, Impls, AllInterfaces) ->
|
Impls :: [aeso_syntax:con()],
|
||||||
Unmatched = match_impls(Env, Decls, ConId, name(IName), Impls),
|
DefinedContracts :: [aeso_syntax:decl()],
|
||||||
check_implemented_interfaces1(Env, Interfaces, ConId, Unmatched, AllInterfaces).
|
InterfaceCon :: aeso_syntax:con(),
|
||||||
|
FunDecl :: aeso_syntax:fundecl().
|
||||||
|
functions_to_implement(Impls, DefinedContracts) ->
|
||||||
|
ImplsNames = [ name(I) || I <- Impls ],
|
||||||
|
Interfaces = [ I || I = {contract_interface, _, Con, _, _} <- DefinedContracts,
|
||||||
|
lists:member(name(Con), ImplsNames) ],
|
||||||
|
|
||||||
%% Match the functions of the contract with the interfaces functions, and return unmatched functions
|
%% All implemented intrefaces should already be defined
|
||||||
match_impls(_, [], _, _, Impls) ->
|
InterfacesNames = [name(element(3, I)) || I <- Interfaces],
|
||||||
Impls;
|
[ begin
|
||||||
match_impls(Env, [{fun_decl, _, {id, _, FunName}, FunType = {fun_t, _, _, ArgsTypes, RetDecl}} | Decls], ConId, IName, Impls) ->
|
Found = lists:member(name(Impl), InterfacesNames),
|
||||||
Match = fun({letfun, _, {id, _, FName}, Args, RetFun, _}) when FName == FunName ->
|
Found orelse type_error({referencing_undefined_interface, Impl})
|
||||||
length(ArgsTypes) == length(Args) andalso
|
end || Impl <- Impls
|
||||||
unify(Env#env{unify_throws = false}, RetDecl, RetFun, unknown) andalso
|
],
|
||||||
lists:all(fun({T1, {typed, _, _, T2}}) -> unify(Env#env{unify_throws = false}, T1, T2, unknown) end,
|
|
||||||
lists:zip(ArgsTypes, Args));
|
lists:flatten([ [ {Con, Decl} || Decl <- Decls] || {contract_interface, _, Con, _, Decls} <- Interfaces ]).
|
||||||
({fun_decl, _, {id, _, FName}, FunT}) when FName == FunName ->
|
|
||||||
unify(Env#env{unify_throws = false}, FunT, FunType, unknown);
|
%% Fill the ets table functions_to_implement with functions from the implemented
|
||||||
(_) -> false
|
%% interfaces Impls.
|
||||||
end,
|
-spec populate_functions_to_implement(env(), ContractName, Impls, DefinedContracts) -> ok | no_return() when
|
||||||
UnmatchedImpls = case lists:search(Match, Impls) of
|
ContractName :: aeso_syntax:con(),
|
||||||
{value, V} ->
|
Impls :: [aeso_syntax:con()],
|
||||||
lists:delete(V, Impls);
|
DefinedContracts :: [aeso_syntax:decl()].
|
||||||
false ->
|
populate_functions_to_implement(Env, ContractName, Impls, DefinedContracts) ->
|
||||||
type_error({unimplemented_interface_function, ConId, IName, FunName}),
|
create_type_errors(),
|
||||||
Impls
|
[ begin
|
||||||
end,
|
Inserted = ets_insert_new(functions_to_implement, {name(Id), I, Decl}),
|
||||||
match_impls(Env, Decls, ConId, IName, UnmatchedImpls).
|
[{_, I2, _}] = ets_lookup(functions_to_implement, name(Id)),
|
||||||
|
Inserted orelse type_error({interface_implementation_conflict, ContractName, I, I2, Id})
|
||||||
|
end || {I, Decl = {fun_decl, _, Id, _}} <- functions_to_implement(Impls, DefinedContracts) ],
|
||||||
|
destroy_and_report_type_errors(Env).
|
||||||
|
|
||||||
%% Asserts that the main contract is somehow defined.
|
%% Asserts that the main contract is somehow defined.
|
||||||
identify_main_contract(Contracts, Options) ->
|
identify_main_contract(Contracts, Options) ->
|
||||||
@@ -1367,7 +1406,7 @@ check_named_arg(Env, {named_arg_t, Ann, Id, Type, Default}) ->
|
|||||||
-spec check_fields(env(), #{ name() => aeso_syntax:decl() }, type(), [aeso_syntax:field_t()]) -> env().
|
-spec check_fields(env(), #{ name() => aeso_syntax:decl() }, type(), [aeso_syntax:field_t()]) -> env().
|
||||||
check_fields(Env, _TypeMap, _, []) -> Env;
|
check_fields(Env, _TypeMap, _, []) -> Env;
|
||||||
check_fields(Env, TypeMap, RecTy, [{field_t, Ann, Id, Type} | Fields]) ->
|
check_fields(Env, TypeMap, RecTy, [{field_t, Ann, Id, Type} | Fields]) ->
|
||||||
Env1 = bind_field(name(Id), #field_info{ ann = Ann, kind = record, field_t = Type, record_t = RecTy }, Env),
|
Env1 = bind_field_append(name(Id), #field_info{ ann = Ann, kind = record, field_t = Type, record_t = RecTy }, Env),
|
||||||
check_fields(Env1, TypeMap, RecTy, Fields).
|
check_fields(Env1, TypeMap, RecTy, Fields).
|
||||||
|
|
||||||
check_parameterizable({id, Ann, "event"}, [_ | _]) ->
|
check_parameterizable({id, Ann, "event"}, [_ | _]) ->
|
||||||
@@ -1466,15 +1505,32 @@ check_reserved_entrypoints(Funs) ->
|
|||||||
-spec check_fundecl(env(), aeso_syntax:decl()) -> {{name(), typesig()}, aeso_syntax:decl()}.
|
-spec check_fundecl(env(), aeso_syntax:decl()) -> {{name(), typesig()}, aeso_syntax:decl()}.
|
||||||
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),
|
||||||
{{Name, {type_sig, Ann, none, Named, Args, Ret}}, {fun_decl, Ann, Id, Type1}};
|
TypeSig = {type_sig, Ann, none, Named, Args, Ret},
|
||||||
|
register_implementation(Name),
|
||||||
|
{{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}),
|
||||||
{{Name, {type_sig, Ann, none, [], [], Type}}, check_type(Env, Type)}.
|
{{Name, {type_sig, Ann, none, [], [], Type}}, check_type(Env, Type)}.
|
||||||
|
|
||||||
|
%% Register the function FunName as implemented by deleting it from the functions
|
||||||
|
%% to be implemented table if it is included there, or return true otherwise.
|
||||||
|
-spec register_implementation(FunName) -> true | no_return() when
|
||||||
|
FunName :: string().
|
||||||
|
register_implementation(Name) ->
|
||||||
|
case ets_lookup(functions_to_implement, Name) of
|
||||||
|
[{Name, _, {fun_decl, _, _, _}}] ->
|
||||||
|
ets_delete(functions_to_implement, Name);
|
||||||
|
[] ->
|
||||||
|
true;
|
||||||
|
_ ->
|
||||||
|
error("Ets set has multiple keys")
|
||||||
|
end.
|
||||||
|
|
||||||
infer_nonrec(Env, LetFun) ->
|
infer_nonrec(Env, LetFun) ->
|
||||||
create_constraints(),
|
create_constraints(),
|
||||||
NewLetFun = infer_letfun(Env, LetFun),
|
NewLetFun = {{FunName, _}, _} = infer_letfun(Env, LetFun),
|
||||||
check_special_funs(Env, NewLetFun),
|
check_special_funs(Env, NewLetFun),
|
||||||
|
register_implementation(FunName),
|
||||||
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),
|
||||||
@@ -1504,6 +1560,7 @@ infer_letrec(Env, Defs) ->
|
|||||||
Inferred =
|
Inferred =
|
||||||
[ begin
|
[ begin
|
||||||
Res = {{Name, TypeSig}, _} = infer_letfun(ExtendEnv, LF),
|
Res = {{Name, TypeSig}, _} = infer_letfun(ExtendEnv, LF),
|
||||||
|
register_implementation(Name),
|
||||||
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}),
|
||||||
@@ -1842,7 +1899,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 ],
|
||||||
@@ -2129,6 +2186,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 == '>';
|
||||||
@@ -2156,6 +2218,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}.
|
||||||
@@ -2204,7 +2269,7 @@ next_count() ->
|
|||||||
ets_tables() ->
|
ets_tables() ->
|
||||||
[options, type_vars, constraints, freshen_tvars, type_errors,
|
[options, type_vars, constraints, freshen_tvars, type_errors,
|
||||||
defined_contracts, warnings, function_calls, all_functions,
|
defined_contracts, warnings, function_calls, all_functions,
|
||||||
type_vars_variance].
|
type_vars_variance, functions_to_implement].
|
||||||
|
|
||||||
clean_up_ets() ->
|
clean_up_ets() ->
|
||||||
[ catch ets_delete(Tab) || Tab <- ets_tables() ],
|
[ catch ets_delete(Tab) || Tab <- ets_tables() ],
|
||||||
@@ -2240,10 +2305,18 @@ ets_delete(Name) ->
|
|||||||
put(aeso_ast_infer_types, maps:remove(Name, Tabs)),
|
put(aeso_ast_infer_types, maps:remove(Name, Tabs)),
|
||||||
ets:delete(TabId).
|
ets:delete(TabId).
|
||||||
|
|
||||||
|
ets_delete(Name, Key) ->
|
||||||
|
TabId = ets_tabid(Name),
|
||||||
|
ets:delete(TabId, Key).
|
||||||
|
|
||||||
ets_insert(Name, Object) ->
|
ets_insert(Name, Object) ->
|
||||||
TabId = ets_tabid(Name),
|
TabId = ets_tabid(Name),
|
||||||
ets:insert(TabId, Object).
|
ets:insert(TabId, Object).
|
||||||
|
|
||||||
|
ets_insert_new(Name, Object) ->
|
||||||
|
TabId = ets_tabid(Name),
|
||||||
|
ets:insert_new(TabId, Object).
|
||||||
|
|
||||||
ets_lookup(Name, Key) ->
|
ets_lookup(Name, Key) ->
|
||||||
TabId = ets_tabid(Name),
|
TabId = ets_tabid(Name),
|
||||||
ets:lookup(TabId, Key).
|
ets:lookup(TabId, Key).
|
||||||
@@ -2310,7 +2383,12 @@ solve_constraints(Env) ->
|
|||||||
field_t = FieldType,
|
field_t = FieldType,
|
||||||
kind = Kind,
|
kind = Kind,
|
||||||
context = When }) ->
|
context = When }) ->
|
||||||
case lookup_record_field(Env, FieldName, Kind) of
|
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}),
|
type_error({undefined_field, Field}),
|
||||||
false;
|
false;
|
||||||
@@ -2805,6 +2883,8 @@ 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) ->
|
||||||
|
type_error({unify_varargs, When});
|
||||||
unify1(Env, {uvar, A, R}, T, _Variance, When) ->
|
unify1(Env, {uvar, A, R}, T, _Variance, When) ->
|
||||||
case occurs_check(R, T) of
|
case occurs_check(R, T) of
|
||||||
true ->
|
true ->
|
||||||
@@ -2828,6 +2908,12 @@ 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) andalso 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)
|
||||||
|
when Variance == covariant orelse Variance == bivariant ->
|
||||||
|
true;
|
||||||
|
unify1(_Env, {id, _, "void"}, _B, Variance, _When)
|
||||||
|
when Variance == contravariant orelse Variance == bivariant ->
|
||||||
|
true;
|
||||||
unify1(_Env, {id, _, Name}, {id, _, Name}, _Variance, _When) ->
|
unify1(_Env, {id, _, Name}, {id, _, Name}, _Variance, _When) ->
|
||||||
true;
|
true;
|
||||||
unify1(Env, A = {con, _, NameA}, B = {con, _, NameB}, Variance, When) ->
|
unify1(Env, A = {con, _, NameA}, B = {con, _, NameB}, Variance, When) ->
|
||||||
@@ -3568,7 +3654,7 @@ mk_error({multiple_main_contracts, Ann}) ->
|
|||||||
Msg = "Only one main contract can be defined.",
|
Msg = "Only one main contract can be defined.",
|
||||||
mk_t_err(pos(Ann), Msg);
|
mk_t_err(pos(Ann), Msg);
|
||||||
mk_error({unify_varargs, When}) ->
|
mk_error({unify_varargs, When}) ->
|
||||||
Msg = "Cannot unify variable argument list.",
|
Msg = "Cannot infer types for variable argument list.",
|
||||||
{Pos, Ctxt} = pp_when(When),
|
{Pos, Ctxt} = pp_when(When),
|
||||||
mk_t_err(Pos, Msg, Ctxt);
|
mk_t_err(Pos, Msg, Ctxt);
|
||||||
mk_error({clone_no_contract, Ann}) ->
|
mk_error({clone_no_contract, Ann}) ->
|
||||||
@@ -3632,7 +3718,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)]),
|
||||||
@@ -3643,6 +3729,11 @@ mk_error({invalid_oracle_type, Why, What, Ann, Type}) ->
|
|||||||
Msg = io_lib:format("Invalid oracle type\n~s`", [pp_type(" `", Type)]),
|
Msg = io_lib:format("Invalid oracle type\n~s`", [pp_type(" `", Type)]),
|
||||||
Cxt = io_lib:format("The ~s type must not be ~s", [What, WhyS]),
|
Cxt = io_lib:format("The ~s type must not be ~s", [What, WhyS]),
|
||||||
mk_t_err(pos(Ann), Msg, Cxt);
|
mk_t_err(pos(Ann), Msg, Cxt);
|
||||||
|
mk_error({interface_implementation_conflict, Contract, I1, I2, Fun}) ->
|
||||||
|
Msg = io_lib:format("Both interfaces `~s` and `~s` implemented by "
|
||||||
|
"the contract `~s` have a function called `~s`",
|
||||||
|
[name(I1), name(I2), name(Contract), name(Fun)]),
|
||||||
|
mk_t_err(pos(Contract), Msg);
|
||||||
mk_error(Err) ->
|
mk_error(Err) ->
|
||||||
Msg = io_lib:format("Unknown error: ~p", [Err]),
|
Msg = io_lib:format("Unknown error: ~p", [Err]),
|
||||||
mk_t_err(pos(0, 0), Msg).
|
mk_t_err(pos(0, 0), Msg).
|
||||||
|
|||||||
+88
-43
@@ -32,12 +32,13 @@
|
|||||||
|
|
||||||
-type op() :: '+' | '-' | '*' | '/' | mod | '^' | '++' | '::' |
|
-type op() :: '+' | '-' | '*' | '/' | mod | '^' | '++' | '::' |
|
||||||
'<' | '>' | '=<' | '>=' | '==' | '!=' | '!' |
|
'<' | '>' | '=<' | '>=' | '==' | '!=' | '!' |
|
||||||
|
'band' | 'bor' | 'bxor' | 'bnot' | '<<' | '>>' |
|
||||||
map_get | map_get_d | map_set | map_from_list | map_to_list |
|
map_get | map_get_d | map_set | map_from_list | map_to_list |
|
||||||
map_delete | map_member | map_size | string_length |
|
map_delete | map_member | map_size | string_length |
|
||||||
string_concat | bits_set | bits_clear | bits_test | bits_sum |
|
string_concat | bits_set | bits_clear | bits_test | bits_sum |
|
||||||
bits_intersection | bits_union | bits_difference |
|
bits_intersection | bits_union | bits_difference |
|
||||||
contract_to_address | address_to_contract | crypto_verify_sig | crypto_verify_sig_secp256k1 |
|
contract_to_address | address_to_contract | crypto_verify_sig | crypto_verify_sig_secp256k1 |
|
||||||
crypto_sha3 | crypto_sha256 | crypto_blake2b |
|
crypto_sha3 | crypto_sha256 | crypto_blake2b | crypto_poseidon |
|
||||||
crypto_ecverify_secp256k1 | crypto_ecrecover_secp256k1 |
|
crypto_ecverify_secp256k1 | crypto_ecrecover_secp256k1 |
|
||||||
mcl_bls12_381_g1_neg | mcl_bls12_381_g1_norm | mcl_bls12_381_g1_valid |
|
mcl_bls12_381_g1_neg | mcl_bls12_381_g1_norm | mcl_bls12_381_g1_valid |
|
||||||
mcl_bls12_381_g1_is_zero | mcl_bls12_381_g1_add | mcl_bls12_381_g1_mul |
|
mcl_bls12_381_g1_is_zero | mcl_bls12_381_g1_add | mcl_bls12_381_g1_mul |
|
||||||
@@ -149,17 +150,18 @@
|
|||||||
|
|
||||||
-type state_layout() :: {tuple, [state_layout()]} | {reg, state_reg()}.
|
-type state_layout() :: {tuple, [state_layout()]} | {reg, state_reg()}.
|
||||||
|
|
||||||
-type env() :: #{ type_env := type_env(),
|
-type env() :: #{ type_env := type_env(),
|
||||||
fun_env := fun_env(),
|
fun_env := fun_env(),
|
||||||
con_env := con_env(),
|
con_env := con_env(),
|
||||||
child_con_env := child_con_env(),
|
child_con_env := child_con_env(),
|
||||||
event_type => aeso_syntax:typedef(),
|
event_type => aeso_syntax:typedef(),
|
||||||
builtins := builtins(),
|
builtins := builtins(),
|
||||||
options := [option()],
|
options := [option()],
|
||||||
state_layout => state_layout(),
|
state_layout => state_layout(),
|
||||||
context => context(),
|
context => context(),
|
||||||
vars => [var_name()],
|
vars => [var_name()],
|
||||||
functions := #{ fun_name() => fun_def() }
|
functions := #{ fun_name() => fun_def() },
|
||||||
|
saved_fresh_names => #{ var_name() => var_name() }
|
||||||
}.
|
}.
|
||||||
|
|
||||||
-define(HASH_BYTES, 32).
|
-define(HASH_BYTES, 32).
|
||||||
@@ -170,7 +172,7 @@
|
|||||||
%% and produces Fate intermediate code.
|
%% and produces Fate intermediate code.
|
||||||
-spec ast_to_fcode(aeso_syntax:ast(), [option()]) -> {env(), fcode()}.
|
-spec ast_to_fcode(aeso_syntax:ast(), [option()]) -> {env(), fcode()}.
|
||||||
ast_to_fcode(Code, Options) ->
|
ast_to_fcode(Code, Options) ->
|
||||||
init_fresh_names(),
|
init_fresh_names(Options),
|
||||||
{Env1, FCode1} = to_fcode(init_env(Options), Code),
|
{Env1, FCode1} = to_fcode(init_env(Options), Code),
|
||||||
FCode2 = optimize(FCode1, Options),
|
FCode2 = optimize(FCode1, Options),
|
||||||
Env2 = Env1#{ child_con_env :=
|
Env2 = Env1#{ child_con_env :=
|
||||||
@@ -178,13 +180,18 @@ ast_to_fcode(Code, Options) ->
|
|||||||
fun (_, FC) -> optimize(FC, Options) end,
|
fun (_, FC) -> optimize(FC, Options) end,
|
||||||
maps:get(child_con_env, Env1)
|
maps:get(child_con_env, Env1)
|
||||||
)},
|
)},
|
||||||
clear_fresh_names(),
|
Env3 =
|
||||||
{Env2, FCode2}.
|
case proplists:get_value(debug_info, Options, false) of
|
||||||
|
true -> Env2#{ saved_fresh_names => get(saved_fresh_names) };
|
||||||
|
false -> Env2
|
||||||
|
end,
|
||||||
|
clear_fresh_names(Options),
|
||||||
|
{Env3, FCode2}.
|
||||||
|
|
||||||
optimize(FCode1, Options) ->
|
optimize(FCode1, Options) ->
|
||||||
Verbose = lists:member(pp_fcode, Options),
|
Verbose = lists:member(pp_fcode, Options),
|
||||||
[io:format("-- Before lambda lifting --\n~s\n\n", [format_fcode(FCode1)]) || Verbose],
|
[io:format("-- Before lambda lifting --\n~s\n\n", [format_fcode(FCode1)]) || Verbose],
|
||||||
FCode2 = optimize_fcode(FCode1),
|
FCode2 = optimize_fcode(FCode1, Options),
|
||||||
[ io:format("-- After optimization --\n~s\n\n", [format_fcode(FCode2)]) || Verbose, FCode2 /= FCode1 ],
|
[ io:format("-- After optimization --\n~s\n\n", [format_fcode(FCode2)]) || Verbose, FCode2 /= FCode1 ],
|
||||||
FCode3 = lambda_lift(FCode2),
|
FCode3 = lambda_lift(FCode2),
|
||||||
[ io:format("-- After lambda lifting --\n~s\n\n", [format_fcode(FCode3)]) || Verbose, FCode3 /= FCode2 ],
|
[ io:format("-- After lambda lifting --\n~s\n\n", [format_fcode(FCode3)]) || Verbose, FCode3 /= FCode2 ],
|
||||||
@@ -208,6 +215,12 @@ init_env(Options) ->
|
|||||||
["AENS", "ContractPt"] => #con_tag{ tag = 2, arities = [1, 1, 1, 1] },
|
["AENS", "ContractPt"] => #con_tag{ tag = 2, arities = [1, 1, 1, 1] },
|
||||||
["AENS", "ChannelPt"] => #con_tag{ tag = 3, arities = [1, 1, 1, 1] },
|
["AENS", "ChannelPt"] => #con_tag{ tag = 3, arities = [1, 1, 1, 1] },
|
||||||
["AENS", "Name"] => #con_tag{ tag = 0, arities = [3] },
|
["AENS", "Name"] => #con_tag{ tag = 0, arities = [3] },
|
||||||
|
["AENSv2", "AccountPt"] => #con_tag{ tag = 0, arities = [1, 1, 1, 1, 1] },
|
||||||
|
["AENSv2", "OraclePt"] => #con_tag{ tag = 1, arities = [1, 1, 1, 1, 1] },
|
||||||
|
["AENSv2", "ContractPt"] => #con_tag{ tag = 2, arities = [1, 1, 1, 1, 1] },
|
||||||
|
["AENSv2", "ChannelPt"] => #con_tag{ tag = 3, arities = [1, 1, 1, 1, 1] },
|
||||||
|
["AENSv2", "DataPt"] => #con_tag{ tag = 4, arities = [1, 1, 1, 1, 1] },
|
||||||
|
["AENSv2", "Name"] => #con_tag{ tag = 0, arities = [3] },
|
||||||
["Chain", "GAMetaTx"] => #con_tag{ tag = 0, arities = [2] },
|
["Chain", "GAMetaTx"] => #con_tag{ tag = 0, arities = [2] },
|
||||||
["Chain", "PayingForTx"] => #con_tag{ tag = 0, arities = [2] },
|
["Chain", "PayingForTx"] => #con_tag{ tag = 0, arities = [2] },
|
||||||
["Chain", "SpendTx"] => #con_tag{ tag = 0, arities = ChainTxArities },
|
["Chain", "SpendTx"] => #con_tag{ tag = 0, arities = ChainTxArities },
|
||||||
@@ -239,8 +252,11 @@ init_env(Options) ->
|
|||||||
|
|
||||||
-spec builtins() -> builtins().
|
-spec builtins() -> builtins().
|
||||||
builtins() ->
|
builtins() ->
|
||||||
MkName = fun(NS, Fun) ->
|
MkName = fun
|
||||||
list_to_atom(string:to_lower(string:join(NS ++ [Fun], "_")))
|
(["AENSv2"], Fun) ->
|
||||||
|
list_to_atom(string:to_lower("AENS_" ++ Fun));
|
||||||
|
(NS, Fun) ->
|
||||||
|
list_to_atom(string:to_lower(string:join(NS ++ [Fun], "_")))
|
||||||
end,
|
end,
|
||||||
Scopes = [{[], [{"abort", 1}, {"require", 2}, {"exit", 1}]},
|
Scopes = [{[], [{"abort", 1}, {"require", 2}, {"exit", 1}]},
|
||||||
{["Chain"], [{"spend", 2}, {"balance", 1}, {"block_hash", 1}, {"coinbase", none},
|
{["Chain"], [{"spend", 2}, {"balance", 1}, {"block_hash", 1}, {"coinbase", none},
|
||||||
@@ -252,13 +268,13 @@ builtins() ->
|
|||||||
{["Oracle"], [{"register", 4}, {"expiry", 1}, {"query_fee", 1}, {"query", 5}, {"get_question", 2},
|
{["Oracle"], [{"register", 4}, {"expiry", 1}, {"query_fee", 1}, {"query", 5}, {"get_question", 2},
|
||||||
{"respond", 4}, {"extend", 3}, {"get_answer", 2},
|
{"respond", 4}, {"extend", 3}, {"get_answer", 2},
|
||||||
{"check", 1}, {"check_query", 2}]},
|
{"check", 1}, {"check_query", 2}]},
|
||||||
{["AENS"], [{"resolve", 2}, {"preclaim", 3}, {"claim", 5}, {"transfer", 4},
|
{["AENSv2"], [{"resolve", 2}, {"preclaim", 3}, {"claim", 5}, {"transfer", 4},
|
||||||
{"revoke", 3}, {"update", 6}, {"lookup", 1}]},
|
{"revoke", 3}, {"update", 6}, {"lookup", 1}]},
|
||||||
{["Map"], [{"from_list", 1}, {"to_list", 1}, {"lookup", 2},
|
{["Map"], [{"from_list", 1}, {"to_list", 1}, {"lookup", 2},
|
||||||
{"lookup_default", 3}, {"delete", 2}, {"member", 2}, {"size", 1}]},
|
{"lookup_default", 3}, {"delete", 2}, {"member", 2}, {"size", 1}]},
|
||||||
{["Crypto"], [{"verify_sig", 3}, {"verify_sig_secp256k1", 3},
|
{["Crypto"], [{"verify_sig", 3}, {"verify_sig_secp256k1", 3},
|
||||||
{"ecverify_secp256k1", 3}, {"ecrecover_secp256k1", 2},
|
{"ecverify_secp256k1", 3}, {"ecrecover_secp256k1", 2},
|
||||||
{"sha3", 1}, {"sha256", 1}, {"blake2b", 1}]},
|
{"sha3", 1}, {"sha256", 1}, {"blake2b", 1}, {"poseidon", 2}]},
|
||||||
{["MCL_BLS12_381"], [{"g1_neg", 1}, {"g1_norm", 1}, {"g1_valid", 1}, {"g1_is_zero", 1}, {"g1_add", 2}, {"g1_mul", 2},
|
{["MCL_BLS12_381"], [{"g1_neg", 1}, {"g1_norm", 1}, {"g1_valid", 1}, {"g1_is_zero", 1}, {"g1_add", 2}, {"g1_mul", 2},
|
||||||
{"g2_neg", 1}, {"g2_norm", 1}, {"g2_valid", 1}, {"g2_is_zero", 1}, {"g2_add", 2}, {"g2_mul", 2},
|
{"g2_neg", 1}, {"g2_norm", 1}, {"g2_valid", 1}, {"g2_is_zero", 1}, {"g2_add", 2}, {"g2_mul", 2},
|
||||||
{"gt_inv", 1}, {"gt_add", 2}, {"gt_mul", 2}, {"gt_pow", 2}, {"gt_is_one", 1},
|
{"gt_inv", 1}, {"gt_add", 2}, {"gt_mul", 2}, {"gt_pow", 2}, {"gt_is_one", 1},
|
||||||
@@ -271,8 +287,9 @@ builtins() ->
|
|||||||
{["Bits"], [{"set", 2}, {"clear", 2}, {"test", 2}, {"sum", 1}, {"intersection", 2},
|
{["Bits"], [{"set", 2}, {"clear", 2}, {"test", 2}, {"sum", 1}, {"intersection", 2},
|
||||||
{"union", 2}, {"difference", 2}, {"none", none}, {"all", none}]},
|
{"union", 2}, {"difference", 2}, {"none", none}, {"all", none}]},
|
||||||
{["Bytes"], [{"to_int", 1}, {"to_str", 1}, {"concat", 2}, {"split", 1}]},
|
{["Bytes"], [{"to_int", 1}, {"to_str", 1}, {"concat", 2}, {"split", 1}]},
|
||||||
{["Int"], [{"to_str", 1}]},
|
{["Int"], [{"to_str", 1}, {"mulmod", 2}]},
|
||||||
{["Address"], [{"to_str", 1}, {"to_contract", 1}, {"is_oracle", 1}, {"is_contract", 1}, {"is_payable", 1}]}
|
{["Address"], [{"to_str", 1}, {"to_bytes", 1}, {"to_contract", 1},
|
||||||
|
{"is_oracle", 1}, {"is_contract", 1}, {"is_payable", 1}]}
|
||||||
],
|
],
|
||||||
maps:from_list([ {NS ++ [Fun], {MkName(NS, Fun), Arity}}
|
maps:from_list([ {NS ++ [Fun], {MkName(NS, Fun), Arity}}
|
||||||
|| {NS, Funs} <- Scopes,
|
|| {NS, Funs} <- Scopes,
|
||||||
@@ -307,11 +324,13 @@ init_type_env() ->
|
|||||||
["Chain", "ttl"] => ?type({variant, [[integer], [integer]]}),
|
["Chain", "ttl"] => ?type({variant, [[integer], [integer]]}),
|
||||||
["AENS", "pointee"] => ?type({variant, [[address], [address], [address], [address]]}),
|
["AENS", "pointee"] => ?type({variant, [[address], [address], [address], [address]]}),
|
||||||
["AENS", "name"] => ?type({variant, [[address, {variant, [[integer], [integer]]}, {map, string, {variant, [[address], [address], [address], [address]]}}]]}),
|
["AENS", "name"] => ?type({variant, [[address, {variant, [[integer], [integer]]}, {map, string, {variant, [[address], [address], [address], [address]]}}]]}),
|
||||||
|
["AENSv2", "pointee"] => ?type({variant, [[address], [address], [address], [address], [string]]}),
|
||||||
|
["AENSv2", "name"] => ?type({variant, [[address, {variant, [[integer], [integer]]}, {map, string, {variant, [[address], [address], [address], [address], [string]]}}]]}),
|
||||||
["Chain", "ga_meta_tx"] => ?type({variant, [[address, integer]]}),
|
["Chain", "ga_meta_tx"] => ?type({variant, [[address, integer]]}),
|
||||||
["Chain", "paying_for_tx"] => ?type({variant, [[address, integer]]}),
|
["Chain", "paying_for_tx"] => ?type({variant, [[address, integer]]}),
|
||||||
["Chain", "base_tx"] => ?type(BaseTx),
|
["Chain", "base_tx"] => ?type(BaseTx),
|
||||||
["MCL_BLS12_381", "fr"] => ?type({bytes, 32}),
|
["MCL_BLS12_381", "fr"] => ?type({bytes, 32}),
|
||||||
["MCL_BLS12_381", "fp"] => ?type({bytes, 48})
|
["MCL_BLS12_381", "fp"] => ?type({bytes, 48})
|
||||||
}.
|
}.
|
||||||
|
|
||||||
is_no_code(Env) ->
|
is_no_code(Env) ->
|
||||||
@@ -688,8 +707,9 @@ expr_to_fcode(Env, Type, {app, Ann, {Op, _}, [A, B]}) when is_atom(Op) ->
|
|||||||
end;
|
end;
|
||||||
expr_to_fcode(Env, _Type, {app, _Ann, {Op, _}, [A]}) when is_atom(Op) ->
|
expr_to_fcode(Env, _Type, {app, _Ann, {Op, _}, [A]}) when is_atom(Op) ->
|
||||||
case Op of
|
case Op of
|
||||||
'-' -> {op, '-', [{lit, {int, 0}}, expr_to_fcode(Env, A)]};
|
'-' -> {op, '-', [{lit, {int, 0}}, expr_to_fcode(Env, A)]};
|
||||||
'!' -> {op, '!', [expr_to_fcode(Env, A)]}
|
'bnot' -> {op, 'bnot', [expr_to_fcode(Env, A)]};
|
||||||
|
'!' -> {op, '!', [expr_to_fcode(Env, A)]}
|
||||||
end;
|
end;
|
||||||
|
|
||||||
%% Function calls
|
%% Function calls
|
||||||
@@ -1080,9 +1100,9 @@ op_builtins() ->
|
|||||||
stringinternal_sha3, stringinternal_sha256, stringinternal_blake2b,
|
stringinternal_sha3, stringinternal_sha256, stringinternal_blake2b,
|
||||||
char_to_int, char_from_int, stringinternal_to_lower, stringinternal_to_upper,
|
char_to_int, char_from_int, stringinternal_to_lower, stringinternal_to_upper,
|
||||||
bits_set, bits_clear, bits_test, bits_sum, bits_intersection, bits_union,
|
bits_set, bits_clear, bits_test, bits_sum, bits_intersection, bits_union,
|
||||||
bits_difference, int_to_str, address_to_str, crypto_verify_sig,
|
bits_difference, int_to_str, int_mulmod, address_to_str, address_to_bytes, crypto_verify_sig,
|
||||||
address_to_contract,
|
address_to_contract,
|
||||||
crypto_verify_sig_secp256k1, crypto_sha3, crypto_sha256, crypto_blake2b,
|
crypto_verify_sig_secp256k1, crypto_sha3, crypto_sha256, crypto_blake2b, crypto_poseidon,
|
||||||
crypto_ecverify_secp256k1, crypto_ecrecover_secp256k1,
|
crypto_ecverify_secp256k1, crypto_ecrecover_secp256k1,
|
||||||
mcl_bls12_381_g1_neg, mcl_bls12_381_g1_norm, mcl_bls12_381_g1_valid,
|
mcl_bls12_381_g1_neg, mcl_bls12_381_g1_norm, mcl_bls12_381_g1_valid,
|
||||||
mcl_bls12_381_g1_is_zero, mcl_bls12_381_g1_add, mcl_bls12_381_g1_mul,
|
mcl_bls12_381_g1_is_zero, mcl_bls12_381_g1_add, mcl_bls12_381_g1_mul,
|
||||||
@@ -1287,20 +1307,28 @@ lambda_lift_exprs(Layout, As) -> [lambda_lift_expr(Layout, A) || A <- As].
|
|||||||
%% - Constant propagation
|
%% - Constant propagation
|
||||||
%% - Inlining
|
%% - Inlining
|
||||||
|
|
||||||
-spec optimize_fcode(fcode()) -> fcode().
|
-spec optimize_fcode(fcode(), [option()]) -> fcode().
|
||||||
optimize_fcode(Code = #{ functions := Funs }) ->
|
optimize_fcode(Code = #{ functions := Funs }, Options) ->
|
||||||
Code1 = Code#{ functions := maps:map(fun(Name, Def) -> optimize_fun(Code, Name, Def) end, Funs) },
|
Code1 = Code#{ functions := maps:map(fun(Name, Def) -> optimize_fun(Code, Name, Def, Options) end, Funs) },
|
||||||
eliminate_dead_code(Code1).
|
eliminate_dead_code(Code1).
|
||||||
|
|
||||||
-spec optimize_fun(fcode(), fun_name(), fun_def()) -> fun_def().
|
-spec optimize_fun(fcode(), fun_name(), fun_def(), [option()]) -> fun_def().
|
||||||
optimize_fun(Fcode, Fun, Def = #{ body := Body }) ->
|
optimize_fun(Fcode, Fun, Def = #{ body := Body0 }, Options) ->
|
||||||
%% io:format("Optimizing ~p =\n~s\n", [_Fun, prettypr:format(pp_fexpr(_Body))]),
|
Inliner = proplists:get_value(optimize_inliner, Options, true),
|
||||||
Def#{ body := drop_unused_lets(
|
InlineLocalFunctions = proplists:get_value(optimize_inline_local_functions, Options, true),
|
||||||
simplifier(
|
BindSubexpressions = proplists:get_value(optimize_bind_subexpressions, Options, true),
|
||||||
let_floating(
|
LetFloating = proplists:get_value(optimize_let_floating, Options, true),
|
||||||
bind_subexpressions(
|
Simplifier = proplists:get_value(optimize_simplifier, Options, true),
|
||||||
inline_local_functions(
|
DropUnusedLets = proplists:get_value(optimize_drop_unused_lets, Options, true),
|
||||||
inliner(Fcode, Fun, Body)))))) }.
|
|
||||||
|
Body1 = if Inliner -> inliner (Fcode, Fun, Body0); true -> Body0 end,
|
||||||
|
Body2 = if InlineLocalFunctions -> inline_local_functions(Body1); true -> Body1 end,
|
||||||
|
Body3 = if BindSubexpressions -> bind_subexpressions (Body2); true -> Body2 end,
|
||||||
|
Body4 = if LetFloating -> let_floating (Body3); true -> Body3 end,
|
||||||
|
Body5 = if Simplifier -> simplifier (Body4); true -> Body4 end,
|
||||||
|
Body6 = if DropUnusedLets -> drop_unused_lets (Body5); true -> Body5 end,
|
||||||
|
|
||||||
|
Def#{ body := Body6 }.
|
||||||
|
|
||||||
%% --- Inlining ---
|
%% --- Inlining ---
|
||||||
|
|
||||||
@@ -1720,12 +1748,29 @@ resolve_fun(#{ fun_env := Funs, builtins := Builtin } = Env, Q) ->
|
|||||||
{{Fun, Ar}, _} -> {def_u, Fun, Ar}
|
{{Fun, Ar}, _} -> {def_u, Fun, Ar}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
init_fresh_names() ->
|
init_fresh_names(Options) ->
|
||||||
|
proplists:get_value(debug_info, Options, false) andalso init_saved_fresh_names(),
|
||||||
put('%fresh', 0).
|
put('%fresh', 0).
|
||||||
|
|
||||||
clear_fresh_names() ->
|
clear_fresh_names(Options) ->
|
||||||
|
proplists:get_value(debug_info, Options, false) andalso clear_saved_fresh_names(),
|
||||||
erase('%fresh').
|
erase('%fresh').
|
||||||
|
|
||||||
|
init_saved_fresh_names() ->
|
||||||
|
put(saved_fresh_names, #{}).
|
||||||
|
|
||||||
|
clear_saved_fresh_names() ->
|
||||||
|
erase(saved_fresh_names).
|
||||||
|
|
||||||
|
-spec fresh_name_save(string()) -> var_name().
|
||||||
|
fresh_name_save(Name) ->
|
||||||
|
Fresh = fresh_name(),
|
||||||
|
case get(saved_fresh_names) of
|
||||||
|
undefined -> ok;
|
||||||
|
Old -> put(saved_fresh_names, Old#{Fresh => Name})
|
||||||
|
end,
|
||||||
|
Fresh.
|
||||||
|
|
||||||
-spec fresh_name() -> var_name().
|
-spec fresh_name() -> var_name().
|
||||||
fresh_name() -> fresh_name("%").
|
fresh_name() -> fresh_name("%").
|
||||||
|
|
||||||
@@ -1854,7 +1899,7 @@ bottom_up(F, Env, Expr) ->
|
|||||||
(_) -> true end,
|
(_) -> true end,
|
||||||
case ShouldFreshen(X) of
|
case ShouldFreshen(X) of
|
||||||
true ->
|
true ->
|
||||||
Z = fresh_name(),
|
Z = fresh_name_save(X),
|
||||||
Env1 = Env#{ Z => E1 },
|
Env1 = Env#{ Z => E1 },
|
||||||
{'let', Z, E1, bottom_up(F, Env1, rename([{X, Z}], Body))};
|
{'let', Z, E1, bottom_up(F, Env1, rename([{X, Z}], Body))};
|
||||||
false ->
|
false ->
|
||||||
|
|||||||
+12
-4
@@ -112,10 +112,12 @@ from_string(ContractString, Options) ->
|
|||||||
|
|
||||||
from_string1(ContractString, Options) ->
|
from_string1(ContractString, Options) ->
|
||||||
#{ fcode := FCode
|
#{ fcode := FCode
|
||||||
, fcode_env := #{child_con_env := ChildContracts}
|
, fcode_env := FCodeEnv
|
||||||
, folded_typed_ast := FoldedTypedAst
|
, folded_typed_ast := FoldedTypedAst
|
||||||
, warnings := Warnings } = string_to_code(ContractString, Options),
|
, warnings := Warnings } = string_to_code(ContractString, Options),
|
||||||
FateCode = aeso_fcode_to_fate:compile(ChildContracts, FCode, Options),
|
#{ child_con_env := ChildContracts } = FCodeEnv,
|
||||||
|
SavedFreshNames = maps:get(saved_fresh_names, FCodeEnv, #{}),
|
||||||
|
{FateCode, VarsRegs} = 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(),
|
||||||
@@ -128,7 +130,13 @@ from_string1(ContractString, Options) ->
|
|||||||
payable => maps:get(payable, FCode),
|
payable => maps:get(payable, FCode),
|
||||||
warnings => Warnings
|
warnings => Warnings
|
||||||
},
|
},
|
||||||
{ok, maybe_generate_aci(Res, FoldedTypedAst, Options)}.
|
ResDbg = Res#{variables_registers => VarsRegs},
|
||||||
|
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
|
||||||
@@ -185,7 +193,7 @@ check_call1(ContractString0, FunName, Args, Options) ->
|
|||||||
#{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(ContractString0, 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,
|
||||||
|
|||||||
+141
-75
@@ -9,7 +9,7 @@
|
|||||||
%%%-------------------------------------------------------------------
|
%%%-------------------------------------------------------------------
|
||||||
-module(aeso_fcode_to_fate).
|
-module(aeso_fcode_to_fate).
|
||||||
|
|
||||||
-export([compile/2, compile/3, term_to_fate/1, term_to_fate/2]).
|
-export([compile/3, compile/4, term_to_fate/1, term_to_fate/2]).
|
||||||
|
|
||||||
-ifdef(TEST).
|
-ifdef(TEST).
|
||||||
-export([optimize_fun/4, to_basic_blocks/1]).
|
-export([optimize_fun/4, to_basic_blocks/1]).
|
||||||
@@ -45,7 +45,14 @@
|
|||||||
-define(s(N), {store, N}).
|
-define(s(N), {store, N}).
|
||||||
-define(void, {var, 9999}).
|
-define(void, {var, 9999}).
|
||||||
|
|
||||||
-record(env, { contract, vars = [], locals = [], current_function, tailpos = true, child_contracts = #{}, options = []}).
|
-record(env, { contract,
|
||||||
|
vars = [],
|
||||||
|
locals = [],
|
||||||
|
current_function,
|
||||||
|
tailpos = true,
|
||||||
|
child_contracts = #{},
|
||||||
|
saved_fresh_names = #{},
|
||||||
|
options = [] }).
|
||||||
|
|
||||||
%% -- Debugging --------------------------------------------------------------
|
%% -- Debugging --------------------------------------------------------------
|
||||||
|
|
||||||
@@ -71,16 +78,27 @@ code_error(Err) ->
|
|||||||
%% -- Main -------------------------------------------------------------------
|
%% -- Main -------------------------------------------------------------------
|
||||||
|
|
||||||
%% @doc Main entry point.
|
%% @doc Main entry point.
|
||||||
compile(FCode, Options) ->
|
compile(FCode, SavedFreshNames, Options) ->
|
||||||
compile(#{}, FCode, Options).
|
compile(#{}, FCode, SavedFreshNames, Options).
|
||||||
compile(ChildContracts, FCode, 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, 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)]),
|
||||||
FateCode.
|
FateCode1 = case proplists:get_value(include_child_contract_symbols, Options, false) of
|
||||||
|
false -> FateCode;
|
||||||
|
true -> add_child_symbols(ChildContracts, FateCode)
|
||||||
|
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)).
|
||||||
@@ -89,22 +107,48 @@ make_function_name(event) -> <<"Chain.event">>;
|
|||||||
make_function_name({entrypoint, Name}) -> Name;
|
make_function_name({entrypoint, Name}) -> Name;
|
||||||
make_function_name({local_fun, Xs}) -> list_to_binary("." ++ string:join(Xs, ".")).
|
make_function_name({local_fun, Xs}) -> list_to_binary("." ++ string:join(Xs, ".")).
|
||||||
|
|
||||||
functions_to_scode(ChildContracts, ContractName, Functions, Options) ->
|
add_child_symbols(ChildContracts, FateCode) ->
|
||||||
|
Funs = lists:flatten([ maps:keys(ChildFuns) || {_, #{functions := ChildFuns}} <- maps:to_list(ChildContracts) ]),
|
||||||
|
Symbols = maps:from_list([ {make_function_id(FName), make_function_name(FName)} || FName <- Funs ]),
|
||||||
|
aeb_fate_code:update_symbols(FateCode, Symbols).
|
||||||
|
|
||||||
|
functions_to_scode(ChildContracts, ContractName, Functions, SavedFreshNames, Options) ->
|
||||||
FunNames = maps:keys(Functions),
|
FunNames = maps:keys(Functions),
|
||||||
maps:from_list(
|
maps:from_list(
|
||||||
[ {make_function_name(Name), function_to_scode(ChildContracts, ContractName, FunNames, Name, Attrs, Args, Body, Type, Options)}
|
[ {make_function_name(Name), function_to_scode(ChildContracts, ContractName, FunNames, Name, Attrs, Args, Body, Type, SavedFreshNames, Options)}
|
||||||
|| {Name, #{args := Args,
|
|| {Name, #{args := Args,
|
||||||
body := Body,
|
body := Body,
|
||||||
attrs := Attrs,
|
attrs := Attrs,
|
||||||
return := Type}} <- maps:to_list(Functions)]).
|
return := Type}} <- maps:to_list(Functions)]).
|
||||||
|
|
||||||
function_to_scode(ChildContracts, ContractName, Functions, Name, Attrs0, Args, Body, ResType, 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 = Attrs0 -- [stateful], %% Only track private and payable from here.
|
||||||
Env = init_env(ChildContracts, ContractName, Functions, Name, Args, Options),
|
Env = init_env(ChildContracts, ContractName, Functions, Name, Args, SavedFreshNames, Options),
|
||||||
|
[ add_variables_register(Env, Arg, Register) ||
|
||||||
|
proplists:get_value(debug_info, Options, false),
|
||||||
|
{Arg, Register} <- Env#env.vars ],
|
||||||
SCode = to_scode(Env, Body),
|
SCode = to_scode(Env, Body),
|
||||||
{Attrs, {ArgTypes, ResType1}, SCode}.
|
{Attrs, {ArgTypes, ResType1}, SCode}.
|
||||||
|
|
||||||
|
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').
|
||||||
|
|
||||||
typesig_to_scode(Args, Res) ->
|
typesig_to_scode(Args, Res) ->
|
||||||
@@ -149,19 +193,21 @@ types_to_scode(Ts) -> lists:map(fun type_to_scode/1, Ts).
|
|||||||
|
|
||||||
%% -- Environment functions --
|
%% -- Environment functions --
|
||||||
|
|
||||||
init_env(ChildContracts, ContractName, FunNames, Name, Args, 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 }.
|
||||||
|
|
||||||
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) ->
|
||||||
@@ -185,9 +231,10 @@ serialize_contract_code(Env, C) ->
|
|||||||
end,
|
end,
|
||||||
case maps:get(C, Cache, none) of
|
case maps:get(C, Cache, none) of
|
||||||
none ->
|
none ->
|
||||||
Options = Env#env.options,
|
Options = Env#env.options,
|
||||||
FCode = maps:get(C, Env#env.child_contracts),
|
SavedFreshNames = Env#env.saved_fresh_names,
|
||||||
FateCode = compile(Env#env.child_contracts, FCode, Options),
|
FCode = maps:get(C, Env#env.child_contracts),
|
||||||
|
{FateCode, _} = compile1(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, ""),
|
||||||
@@ -635,6 +682,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);
|
||||||
@@ -659,7 +712,9 @@ 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_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);
|
||||||
@@ -669,6 +724,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);
|
||||||
@@ -709,7 +765,7 @@ tuple(N) -> aeb_fate_ops:tuple(?a, N).
|
|||||||
|
|
||||||
optimize_scode(Funs, Options) ->
|
optimize_scode(Funs, Options) ->
|
||||||
maps:map(fun(Name, Def) -> optimize_fun(Funs, Name, Def, Options) end,
|
maps:map(fun(Name, Def) -> optimize_fun(Funs, Name, Def, Options) end,
|
||||||
Funs).
|
Funs).
|
||||||
|
|
||||||
flatten(missing) -> missing;
|
flatten(missing) -> missing;
|
||||||
flatten(Code) -> lists:map(fun flatten_s/1, lists:flatten(Code)).
|
flatten(Code) -> lists:map(fun flatten_s/1, lists:flatten(Code)).
|
||||||
@@ -866,6 +922,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]);
|
||||||
@@ -918,12 +981,14 @@ 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]);
|
||||||
@@ -1092,7 +1157,8 @@ simpl_top(I, Code, Options) ->
|
|||||||
simpl_top(0, I, Code, _Options) ->
|
simpl_top(0, I, Code, _Options) ->
|
||||||
code_error({optimizer_out_of_fuel, I, Code});
|
code_error({optimizer_out_of_fuel, I, Code});
|
||||||
simpl_top(Fuel, I, Code, Options) ->
|
simpl_top(Fuel, I, Code, Options) ->
|
||||||
apply_rules(Fuel, rules(), I, Code, Options).
|
Rules = [R || R = {Rule, _} <- rules(), proplists:get_value(Rule, Options, true)],
|
||||||
|
apply_rules(Fuel, Rules, I, Code, Options).
|
||||||
|
|
||||||
apply_rules(Fuel, Rules, I, Code, Options) ->
|
apply_rules(Fuel, Rules, I, Code, Options) ->
|
||||||
Cons = fun(X, Xs) -> simpl_top(Fuel - 1, X, Xs, Options) end,
|
Cons = fun(X, Xs) -> simpl_top(Fuel - 1, X, Xs, Options) end,
|
||||||
@@ -1119,29 +1185,29 @@ apply_rules_once([{RName, Rule} | Rules], I, Code) ->
|
|||||||
-define(RULE(Name), {Name, fun Name/2}).
|
-define(RULE(Name), {Name, fun Name/2}).
|
||||||
|
|
||||||
merge_rules() ->
|
merge_rules() ->
|
||||||
[?RULE(r_push_consume),
|
[?RULE(optimize_push_consume),
|
||||||
?RULE(r_one_shot_var),
|
?RULE(optimize_one_shot_var),
|
||||||
?RULE(r_write_to_dead_var),
|
?RULE(optimize_write_to_dead_var),
|
||||||
?RULE(r_inline_switch_target)
|
?RULE(optimize_inline_switch_target)
|
||||||
].
|
].
|
||||||
|
|
||||||
rules() ->
|
rules() ->
|
||||||
merge_rules() ++
|
merge_rules() ++
|
||||||
[?RULE(r_swap_push),
|
[?RULE(optimize_swap_push),
|
||||||
?RULE(r_swap_pop),
|
?RULE(optimize_swap_pop),
|
||||||
?RULE(r_swap_write),
|
?RULE(optimize_swap_write),
|
||||||
?RULE(r_constant_propagation),
|
?RULE(optimize_constant_propagation),
|
||||||
?RULE(r_prune_impossible_branches),
|
?RULE(optimize_prune_impossible_branches),
|
||||||
?RULE(r_single_successful_branch),
|
?RULE(optimize_single_successful_branch),
|
||||||
?RULE(r_inline_store),
|
?RULE(optimize_inline_store),
|
||||||
?RULE(r_float_switch_body)
|
?RULE(optimize_float_switch_body)
|
||||||
].
|
].
|
||||||
|
|
||||||
%% Removing pushes that are immediately consumed.
|
%% Removing pushes that are immediately consumed.
|
||||||
r_push_consume({i, Ann1, {'STORE', ?a, A}}, Code) ->
|
optimize_push_consume({i, Ann1, {'STORE', ?a, A}}, Code) ->
|
||||||
inline_push(Ann1, A, 0, Code, []);
|
inline_push(Ann1, A, 0, Code, []);
|
||||||
%% Writing directly to memory instead of going through the accumulator.
|
%% Writing directly to memory instead of going through the accumulator.
|
||||||
r_push_consume({i, Ann1, I}, [{i, Ann2, {'STORE', R, ?a}} | Code]) ->
|
optimize_push_consume({i, Ann1, I}, [{i, Ann2, {'STORE', R, ?a}} | Code]) ->
|
||||||
IsPush =
|
IsPush =
|
||||||
case op_view(I) of
|
case op_view(I) of
|
||||||
{_, ?a, _} -> true;
|
{_, ?a, _} -> true;
|
||||||
@@ -1153,7 +1219,7 @@ r_push_consume({i, Ann1, I}, [{i, Ann2, {'STORE', R, ?a}} | Code]) ->
|
|||||||
end,
|
end,
|
||||||
if IsPush -> {[{i, merge_ann(Ann1, Ann2), setelement(2, I, R)}], Code};
|
if IsPush -> {[{i, merge_ann(Ann1, Ann2), setelement(2, I, R)}], Code};
|
||||||
true -> false end;
|
true -> false end;
|
||||||
r_push_consume(_, _) -> false.
|
optimize_push_consume(_, _) -> false.
|
||||||
|
|
||||||
inline_push(Ann, Arg, Stack, [{i, _, switch_body} = AI | Code], Acc) ->
|
inline_push(Ann, Arg, Stack, [{i, _, switch_body} = AI | Code], Acc) ->
|
||||||
{AI1, {i, Ann1, _}} = swap_instrs({i, Ann, {'STORE', ?a, Arg}}, AI),
|
{AI1, {i, Ann1, _}} = swap_instrs({i, Ann, {'STORE', ?a, Arg}}, AI),
|
||||||
@@ -1186,7 +1252,7 @@ split_stack_arg(N, [A | As], Acc) ->
|
|||||||
split_stack_arg(N1, As, [A | Acc]).
|
split_stack_arg(N1, As, [A | Acc]).
|
||||||
|
|
||||||
%% Move PUSHes past non-stack instructions.
|
%% Move PUSHes past non-stack instructions.
|
||||||
r_swap_push(Push = {i, _, PushI}, [I | Code]) ->
|
optimize_swap_push(Push = {i, _, PushI}, [I | Code]) ->
|
||||||
case op_view(PushI) of
|
case op_view(PushI) of
|
||||||
{_, ?a, _} ->
|
{_, ?a, _} ->
|
||||||
case independent(Push, I) of
|
case independent(Push, I) of
|
||||||
@@ -1197,10 +1263,10 @@ r_swap_push(Push = {i, _, PushI}, [I | Code]) ->
|
|||||||
end;
|
end;
|
||||||
_ -> false
|
_ -> false
|
||||||
end;
|
end;
|
||||||
r_swap_push(_, _) -> false.
|
optimize_swap_push(_, _) -> false.
|
||||||
|
|
||||||
%% Move non-stack instruction past POPs.
|
%% Move non-stack instruction past POPs.
|
||||||
r_swap_pop(IA = {i, _, I}, [JA = {i, _, J} | Code]) ->
|
optimize_swap_pop(IA = {i, _, I}, [JA = {i, _, J} | Code]) ->
|
||||||
case independent(IA, JA) of
|
case independent(IA, JA) of
|
||||||
true ->
|
true ->
|
||||||
case {op_view(I), op_view(J)} of
|
case {op_view(I), op_view(J)} of
|
||||||
@@ -1208,7 +1274,7 @@ r_swap_pop(IA = {i, _, I}, [JA = {i, _, J} | Code]) ->
|
|||||||
{_, false} -> false;
|
{_, false} -> false;
|
||||||
{{_, IR, IAs}, {_, RJ, JAs}} ->
|
{{_, IR, IAs}, {_, RJ, JAs}} ->
|
||||||
NonStackI = not lists:member(?a, [IR | IAs]),
|
NonStackI = not lists:member(?a, [IR | IAs]),
|
||||||
%% RJ /= ?a to not conflict with r_swap_push
|
%% RJ /= ?a to not conflict with optimize_swap_push
|
||||||
PopJ = RJ /= ?a andalso lists:member(?a, JAs),
|
PopJ = RJ /= ?a andalso lists:member(?a, JAs),
|
||||||
case NonStackI andalso PopJ of
|
case NonStackI andalso PopJ of
|
||||||
false -> false;
|
false -> false;
|
||||||
@@ -1219,22 +1285,22 @@ r_swap_pop(IA = {i, _, I}, [JA = {i, _, J} | Code]) ->
|
|||||||
end;
|
end;
|
||||||
false -> false
|
false -> false
|
||||||
end;
|
end;
|
||||||
r_swap_pop(_, _) -> false.
|
optimize_swap_pop(_, _) -> false.
|
||||||
|
|
||||||
%% Match up writes to variables with instructions further down.
|
%% Match up writes to variables with instructions further down.
|
||||||
r_swap_write(I = {i, _, _}, [J | Code]) ->
|
optimize_swap_write(I = {i, _, _}, [J | Code]) ->
|
||||||
case {var_writes(I), independent(I, J)} of
|
case {var_writes(I), independent(I, J)} of
|
||||||
{[_], true} ->
|
{[_], true} ->
|
||||||
{J1, I1} = swap_instrs(I, J),
|
{J1, I1} = swap_instrs(I, J),
|
||||||
r_swap_write([J1], I1, Code);
|
optimize_swap_write([J1], I1, Code);
|
||||||
_ -> false
|
_ -> false
|
||||||
end;
|
end;
|
||||||
r_swap_write(_, _) -> false.
|
optimize_swap_write(_, _) -> false.
|
||||||
|
|
||||||
r_swap_write(Pre, I, [{i, _, switch_body} = J | Code]) ->
|
optimize_swap_write(Pre, I, [{i, _, switch_body} = J | Code]) ->
|
||||||
{J1, I1} = swap_instrs(I, J),
|
{J1, I1} = swap_instrs(I, J),
|
||||||
r_swap_write([J1 | Pre], I1, Code);
|
optimize_swap_write([J1 | Pre], I1, Code);
|
||||||
r_swap_write(Pre, I, Code0 = [J | Code]) ->
|
optimize_swap_write(Pre, I, Code0 = [J | Code]) ->
|
||||||
case apply_rules_once(merge_rules(), I, Code0) of
|
case apply_rules_once(merge_rules(), I, Code0) of
|
||||||
{_Rule, New, Rest} ->
|
{_Rule, New, Rest} ->
|
||||||
{lists:reverse(Pre) ++ New, Rest};
|
{lists:reverse(Pre) ++ New, Rest};
|
||||||
@@ -1243,27 +1309,27 @@ r_swap_write(Pre, I, Code0 = [J | Code]) ->
|
|||||||
false -> false;
|
false -> false;
|
||||||
true ->
|
true ->
|
||||||
{J1, I1} = swap_instrs(I, J),
|
{J1, I1} = swap_instrs(I, J),
|
||||||
r_swap_write([J1 | Pre], I1, Code)
|
optimize_swap_write([J1 | Pre], I1, Code)
|
||||||
end
|
end
|
||||||
end;
|
end;
|
||||||
r_swap_write(_, _, _) -> false.
|
optimize_swap_write(_, _, _) -> false.
|
||||||
|
|
||||||
%% Precompute instructions with known values
|
%% Precompute instructions with known values
|
||||||
r_constant_propagation(Cons = {i, Ann1, {'CONS', R, X, Xs}}, [{i, Ann, {'IS_NIL', S, R}} | Code]) ->
|
optimize_constant_propagation(Cons = {i, Ann1, {'CONS', R, X, Xs}}, [{i, Ann, {'IS_NIL', S, R}} | Code]) ->
|
||||||
Store = {i, Ann, {'STORE', S, ?i(false)}},
|
Store = {i, Ann, {'STORE', S, ?i(false)}},
|
||||||
Cons1 = case R of
|
Cons1 = case R of
|
||||||
?a -> {i, Ann1, {'CONS', ?void, X, Xs}};
|
?a -> {i, Ann1, {'CONS', ?void, X, Xs}};
|
||||||
_ -> Cons
|
_ -> Cons
|
||||||
end,
|
end,
|
||||||
{[Cons1, Store], Code};
|
{[Cons1, Store], Code};
|
||||||
r_constant_propagation(Nil = {i, Ann1, {'NIL', R}}, [{i, Ann, {'IS_NIL', S, R}} | Code]) ->
|
optimize_constant_propagation(Nil = {i, Ann1, {'NIL', R}}, [{i, Ann, {'IS_NIL', S, R}} | Code]) ->
|
||||||
Store = {i, Ann, {'STORE', S, ?i(true)}},
|
Store = {i, Ann, {'STORE', S, ?i(true)}},
|
||||||
Nil1 = case R of
|
Nil1 = case R of
|
||||||
?a -> {i, Ann1, {'NIL', ?void}};
|
?a -> {i, Ann1, {'NIL', ?void}};
|
||||||
_ -> Nil
|
_ -> Nil
|
||||||
end,
|
end,
|
||||||
{[Nil1, Store], Code};
|
{[Nil1, Store], Code};
|
||||||
r_constant_propagation({i, Ann, I}, Code) ->
|
optimize_constant_propagation({i, Ann, I}, Code) ->
|
||||||
case op_view(I) of
|
case op_view(I) of
|
||||||
false -> false;
|
false -> false;
|
||||||
{Op, R, As} ->
|
{Op, R, As} ->
|
||||||
@@ -1277,7 +1343,7 @@ r_constant_propagation({i, Ann, I}, Code) ->
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end;
|
end;
|
||||||
r_constant_propagation(_, _) -> false.
|
optimize_constant_propagation(_, _) -> false.
|
||||||
|
|
||||||
eval_op('ADD', [X, Y]) when is_integer(X), is_integer(Y) -> X + Y;
|
eval_op('ADD', [X, Y]) when is_integer(X), is_integer(Y) -> X + Y;
|
||||||
eval_op('SUB', [X, Y]) when is_integer(X), is_integer(Y) -> X - Y;
|
eval_op('SUB', [X, Y]) when is_integer(X), is_integer(Y) -> X - Y;
|
||||||
@@ -1296,12 +1362,12 @@ eval_op('NOT', [false]) -> true;
|
|||||||
eval_op(_, _) -> no_eval. %% TODO: bits?
|
eval_op(_, _) -> no_eval. %% TODO: bits?
|
||||||
|
|
||||||
%% Prune impossible branches from switches
|
%% Prune impossible branches from switches
|
||||||
r_prune_impossible_branches({switch, ?i(V), Type, Alts, missing}, Code) ->
|
optimize_prune_impossible_branches({switch, ?i(V), Type, Alts, missing}, Code) ->
|
||||||
case pick_branch(Type, V, Alts) of
|
case pick_branch(Type, V, Alts) of
|
||||||
false -> false;
|
false -> false;
|
||||||
Alt -> {Alt, Code}
|
Alt -> {Alt, Code}
|
||||||
end;
|
end;
|
||||||
r_prune_impossible_branches({switch, ?i(V), boolean, [False, True] = Alts, Def}, Code) when V == true; V == false ->
|
optimize_prune_impossible_branches({switch, ?i(V), boolean, [False, True] = Alts, Def}, Code) when V == true; V == false ->
|
||||||
Alts1 = [if V -> missing; true -> False end,
|
Alts1 = [if V -> missing; true -> False end,
|
||||||
if V -> True; true -> missing end],
|
if V -> True; true -> missing end],
|
||||||
case Alts == Alts1 of
|
case Alts == Alts1 of
|
||||||
@@ -1312,7 +1378,7 @@ r_prune_impossible_branches({switch, ?i(V), boolean, [False, True] = Alts, Def},
|
|||||||
_ -> {[{switch, ?i(V), boolean, Alts1, Def}], Code}
|
_ -> {[{switch, ?i(V), boolean, Alts1, Def}], Code}
|
||||||
end
|
end
|
||||||
end;
|
end;
|
||||||
r_prune_impossible_branches(Variant = {i, _, {'VARIANT', R, ?i(_), ?i(Tag), ?i(_)}},
|
optimize_prune_impossible_branches(Variant = {i, _, {'VARIANT', R, ?i(_), ?i(Tag), ?i(_)}},
|
||||||
[{switch, R, Type = {variant, _}, Alts, missing} | Code]) when is_integer(Tag) ->
|
[{switch, R, Type = {variant, _}, Alts, missing} | Code]) when is_integer(Tag) ->
|
||||||
case {R, lists:nth(Tag + 1, Alts)} of
|
case {R, lists:nth(Tag + 1, Alts)} of
|
||||||
{_, missing} ->
|
{_, missing} ->
|
||||||
@@ -1328,7 +1394,7 @@ r_prune_impossible_branches(Variant = {i, _, {'VARIANT', R, ?i(_), ?i(Tag), ?i(_
|
|||||||
false -> {Alt, Code}
|
false -> {Alt, Code}
|
||||||
end
|
end
|
||||||
end;
|
end;
|
||||||
r_prune_impossible_branches(_, _) -> false.
|
optimize_prune_impossible_branches(_, _) -> false.
|
||||||
|
|
||||||
pick_branch(boolean, V, [False, True]) when V == true; V == false ->
|
pick_branch(boolean, V, [False, True]) when V == true; V == false ->
|
||||||
Alt = if V -> True; true -> False end,
|
Alt = if V -> True; true -> False end,
|
||||||
@@ -1341,7 +1407,7 @@ pick_branch(_Type, _V, _Alts) ->
|
|||||||
|
|
||||||
%% If there's a single branch that doesn't abort we can push the code for that
|
%% If there's a single branch that doesn't abort we can push the code for that
|
||||||
%% out of the switch.
|
%% out of the switch.
|
||||||
r_single_successful_branch({switch, R, Type, Alts, Def}, Code) ->
|
optimize_single_successful_branch({switch, R, Type, Alts, Def}, Code) ->
|
||||||
case push_code_out_of_switch([Def | Alts]) of
|
case push_code_out_of_switch([Def | Alts]) of
|
||||||
{_, none} -> false;
|
{_, none} -> false;
|
||||||
{_, many} -> false;
|
{_, many} -> false;
|
||||||
@@ -1349,7 +1415,7 @@ r_single_successful_branch({switch, R, Type, Alts, Def}, Code) ->
|
|||||||
{[Def1 | Alts1], PushedOut} ->
|
{[Def1 | Alts1], PushedOut} ->
|
||||||
{[{switch, R, Type, Alts1, Def1} | PushedOut], Code}
|
{[{switch, R, Type, Alts1, Def1} | PushedOut], Code}
|
||||||
end;
|
end;
|
||||||
r_single_successful_branch(_, _) -> false.
|
optimize_single_successful_branch(_, _) -> false.
|
||||||
|
|
||||||
push_code_out_of_switch([]) -> {[], none};
|
push_code_out_of_switch([]) -> {[], none};
|
||||||
push_code_out_of_switch([Alt | Alts]) ->
|
push_code_out_of_switch([Alt | Alts]) ->
|
||||||
@@ -1385,7 +1451,7 @@ does_abort({switch, _, _, Alts, Def}) ->
|
|||||||
does_abort(_) -> false.
|
does_abort(_) -> false.
|
||||||
|
|
||||||
%% STORE R A, SWITCH R --> SWITCH A
|
%% STORE R A, SWITCH R --> SWITCH A
|
||||||
r_inline_switch_target({i, Ann, {'STORE', R, A}}, [{switch, R, Type, Alts, Def} | Code]) ->
|
optimize_inline_switch_target({i, Ann, {'STORE', R, A}}, [{switch, R, Type, Alts, Def} | Code]) ->
|
||||||
Ann1 =
|
Ann1 =
|
||||||
case is_reg(A) of
|
case is_reg(A) of
|
||||||
true -> Ann#{ live_out := ordsets:add_element(A, maps:get(live_out, Ann)) };
|
true -> Ann#{ live_out := ordsets:add_element(A, maps:get(live_out, Ann)) };
|
||||||
@@ -1404,18 +1470,18 @@ r_inline_switch_target({i, Ann, {'STORE', R, A}}, [{switch, R, Type, Alts, Def}
|
|||||||
end;
|
end;
|
||||||
_ -> false %% impossible
|
_ -> false %% impossible
|
||||||
end;
|
end;
|
||||||
r_inline_switch_target(_, _) -> false.
|
optimize_inline_switch_target(_, _) -> false.
|
||||||
|
|
||||||
%% Float switch-body to closest switch
|
%% Float switch-body to closest switch
|
||||||
r_float_switch_body(I = {i, _, _}, [J = {i, _, switch_body} | Code]) ->
|
optimize_float_switch_body(I = {i, _, _}, [J = {i, _, switch_body} | Code]) ->
|
||||||
{J1, I1} = swap_instrs(I, J),
|
{J1, I1} = swap_instrs(I, J),
|
||||||
{[], [J1, I1 | Code]};
|
{[], [J1, I1 | Code]};
|
||||||
r_float_switch_body(_, _) -> false.
|
optimize_float_switch_body(_, _) -> false.
|
||||||
|
|
||||||
%% Inline stores
|
%% Inline stores
|
||||||
r_inline_store({i, _, {'STORE', R, R}}, Code) ->
|
optimize_inline_store({i, _, {'STORE', R, R}}, Code) ->
|
||||||
{[], Code};
|
{[], Code};
|
||||||
r_inline_store(I = {i, _, {'STORE', R = {var, _}, A}}, Code) ->
|
optimize_inline_store(I = {i, _, {'STORE', R = {var, _}, A}}, Code) ->
|
||||||
%% Not when A is var unless updating the annotations properly.
|
%% Not when A is var unless updating the annotations properly.
|
||||||
Inline = case A of
|
Inline = case A of
|
||||||
{arg, _} -> true;
|
{arg, _} -> true;
|
||||||
@@ -1423,13 +1489,13 @@ r_inline_store(I = {i, _, {'STORE', R = {var, _}, A}}, Code) ->
|
|||||||
{store, _} -> true;
|
{store, _} -> true;
|
||||||
_ -> false
|
_ -> false
|
||||||
end,
|
end,
|
||||||
if Inline -> r_inline_store([I], false, R, A, Code);
|
if Inline -> optimize_inline_store([I], false, R, A, Code);
|
||||||
true -> false end;
|
true -> false end;
|
||||||
r_inline_store(_, _) -> false.
|
optimize_inline_store(_, _) -> false.
|
||||||
|
|
||||||
r_inline_store(Acc, Progress, R, A, [I = {i, _, switch_body} | Code]) ->
|
optimize_inline_store(Acc, Progress, R, A, [I = {i, _, switch_body} | Code]) ->
|
||||||
r_inline_store([I | Acc], Progress, R, A, Code);
|
optimize_inline_store([I | Acc], Progress, R, A, Code);
|
||||||
r_inline_store(Acc, Progress, R, A, [{i, Ann, I} | Code]) ->
|
optimize_inline_store(Acc, Progress, R, A, [{i, Ann, I} | Code]) ->
|
||||||
#{ write := W } = attributes(I),
|
#{ write := W } = attributes(I),
|
||||||
Inl = fun(X) when X == R -> A; (X) -> X end,
|
Inl = fun(X) when X == R -> A; (X) -> X end,
|
||||||
case live_in(R, Ann) of
|
case live_in(R, Ann) of
|
||||||
@@ -1449,14 +1515,14 @@ r_inline_store(Acc, Progress, R, A, [{i, Ann, I} | Code]) ->
|
|||||||
case lists:member(W, [R, A]) of
|
case lists:member(W, [R, A]) of
|
||||||
true when Progress1 -> {lists:reverse(Acc1), Code};
|
true when Progress1 -> {lists:reverse(Acc1), Code};
|
||||||
true -> false;
|
true -> false;
|
||||||
false -> r_inline_store(Acc1, Progress1, R, A, Code)
|
false -> optimize_inline_store(Acc1, Progress1, R, A, Code)
|
||||||
end
|
end
|
||||||
end;
|
end;
|
||||||
r_inline_store(Acc, true, _, _, Code) -> {lists:reverse(Acc), Code};
|
optimize_inline_store(Acc, true, _, _, Code) -> {lists:reverse(Acc), Code};
|
||||||
r_inline_store(_, false, _, _, _) -> false.
|
optimize_inline_store(_, false, _, _, _) -> false.
|
||||||
|
|
||||||
%% Shortcut write followed by final read
|
%% Shortcut write followed by final read
|
||||||
r_one_shot_var({i, Ann1, I}, [{i, Ann2, J} | Code]) ->
|
optimize_one_shot_var({i, Ann1, I}, [{i, Ann2, J} | Code]) ->
|
||||||
case op_view(I) of
|
case op_view(I) of
|
||||||
{Op, R = {var, _}, As} ->
|
{Op, R = {var, _}, As} ->
|
||||||
Copy = case J of
|
Copy = case J of
|
||||||
@@ -1470,11 +1536,11 @@ r_one_shot_var({i, Ann1, I}, [{i, Ann2, J} | Code]) ->
|
|||||||
end;
|
end;
|
||||||
_ -> false
|
_ -> false
|
||||||
end;
|
end;
|
||||||
r_one_shot_var(_, _) -> false.
|
optimize_one_shot_var(_, _) -> false.
|
||||||
|
|
||||||
%% Remove writes to dead variables
|
%% Remove writes to dead variables
|
||||||
r_write_to_dead_var({i, _, {'STORE', ?void, ?a}}, _) -> false; %% Avoid looping
|
optimize_write_to_dead_var({i, _, {'STORE', ?void, ?a}}, _) -> false; %% Avoid looping
|
||||||
r_write_to_dead_var({i, Ann, I}, Code) ->
|
optimize_write_to_dead_var({i, Ann, I}, Code) ->
|
||||||
#{ pure := Pure } = attributes(I),
|
#{ pure := Pure } = attributes(I),
|
||||||
case op_view(I) of
|
case op_view(I) of
|
||||||
{_Op, R, As} when R /= ?a, Pure ->
|
{_Op, R, As} when R /= ?a, Pure ->
|
||||||
@@ -1487,7 +1553,7 @@ r_write_to_dead_var({i, Ann, I}, Code) ->
|
|||||||
end;
|
end;
|
||||||
_ -> false
|
_ -> false
|
||||||
end;
|
end;
|
||||||
r_write_to_dead_var(_, _) -> false.
|
optimize_write_to_dead_var(_, _) -> false.
|
||||||
|
|
||||||
op_view({'ABORT', R}) -> {'ABORT', none, [R]};
|
op_view({'ABORT', R}) -> {'ABORT', none, [R]};
|
||||||
op_view({'EXIT', R}) -> {'EXIT', none, [R]};
|
op_view({'EXIT', R}) -> {'EXIT', none, [R]};
|
||||||
|
|||||||
+8
-3
@@ -333,14 +333,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() ->
|
||||||
|
|||||||
+15
-5
@@ -261,6 +261,8 @@ type(Type, Options) ->
|
|||||||
with_options(Options, fun() -> type(Type) end).
|
with_options(Options, fun() -> type(Type) end).
|
||||||
|
|
||||||
-spec type(aeso_syntax:type()) -> doc().
|
-spec type(aeso_syntax:type()) -> doc().
|
||||||
|
type(F = {fun_t, _, _, var_args, _}) ->
|
||||||
|
type(setelement(4, F, [var_args]));
|
||||||
type({fun_t, _, Named, Args, Ret}) ->
|
type({fun_t, _, Named, Args, Ret}) ->
|
||||||
follow(hsep(args_type(Named ++ Args), text("=>")), type(Ret));
|
follow(hsep(args_type(Named ++ Args), text("=>")), type(Ret));
|
||||||
type({type_sig, _, Named, Args, Ret}) ->
|
type({type_sig, _, Named, Args, Ret}) ->
|
||||||
@@ -288,7 +290,9 @@ type(T = {id, _, _}) -> name(T);
|
|||||||
type(T = {qid, _, _}) -> name(T);
|
type(T = {qid, _, _}) -> name(T);
|
||||||
type(T = {con, _, _}) -> name(T);
|
type(T = {con, _, _}) -> name(T);
|
||||||
type(T = {qcon, _, _}) -> name(T);
|
type(T = {qcon, _, _}) -> name(T);
|
||||||
type(T = {tvar, _, _}) -> name(T).
|
type(T = {tvar, _, _}) -> name(T);
|
||||||
|
|
||||||
|
type(var_args) -> text("var_args").
|
||||||
|
|
||||||
-spec args_type([aeso_syntax:type()]) -> doc().
|
-spec args_type([aeso_syntax:type()]) -> doc().
|
||||||
args_type(Args) ->
|
args_type(Args) ->
|
||||||
@@ -432,15 +436,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};
|
||||||
@@ -450,7 +459,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]}.
|
||||||
|
|||||||
+1
-1
@@ -45,7 +45,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, "|"),
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -13,7 +13,7 @@
|
|||||||
-export_type([ann_line/0, ann_col/0, ann_origin/0, ann_format/0, ann/0]).
|
-export_type([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]).
|
-export_type([decl/0, letbind/0, typedef/0, pragma/0, fundecl/0]).
|
||||||
-export_type([arg/0, field_t/0, constructor_t/0, named_arg_t/0]).
|
-export_type([arg/0, field_t/0, constructor_t/0, named_arg_t/0]).
|
||||||
-export_type([type/0, constant/0, expr/0, arg_expr/0, field/1, stmt/0, alt/0, lvalue/0, elim/0, pat/0]).
|
-export_type([type/0, constant/0, expr/0, arg_expr/0, field/1, stmt/0, alt/0, lvalue/0, elim/0, pat/0]).
|
||||||
-export_type([ast/0]).
|
-export_type([ast/0]).
|
||||||
@@ -106,8 +106,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()}
|
||||||
|
|||||||
+16
-1
@@ -88,7 +88,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)]);
|
||||||
@@ -99,6 +99,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, {Val}}} ->
|
||||||
|
App(["AENSv2","AccountPt"], [Chk(Adr, Val)]);
|
||||||
|
{["AENSv2", "pointee"], {variant, [1, 1, 1, 1, 1], 1, {Val}}} ->
|
||||||
|
App(["AENSv2","OraclePt"], [Chk(Adr, Val)]);
|
||||||
|
{["AENSv2", "pointee"], {variant, [1, 1, 1, 1, 1], 2, {Val}}} ->
|
||||||
|
App(["AENSv2","ContractPt"], [Chk(Adr, Val)]);
|
||||||
|
{["AENSv2", "pointee"], {variant, [1, 1, 1, 1, 1], 3, {Val}}} ->
|
||||||
|
App(["AENSv2","ChannelPt"], [Chk(Adr, Val)]);
|
||||||
|
{["AENSv2", "pointee"], {variant, [1, 1, 1, 1, 1], 4, {Val}}} ->
|
||||||
|
App(["AENSv2","DataPt"], [Chk(Str, Val)]);
|
||||||
|
|
||||||
{["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)]);
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{application, aesophia,
|
{application, aesophia,
|
||||||
[{description, "Compiler for Aeternity Sophia language"},
|
[{description, "Compiler for Aeternity Sophia language"},
|
||||||
{vsn, "7.0.1"},
|
{vsn, "8.0.0"},
|
||||||
{registered, []},
|
{registered, []},
|
||||||
{applications,
|
{applications,
|
||||||
[kernel,
|
[kernel,
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ test_cases(1) ->
|
|||||||
" payable stateful entrypoint a(i : int) = i+1\n">>,
|
" payable stateful entrypoint a(i : int) = i+1\n">>,
|
||||||
MapACI = #{contract =>
|
MapACI = #{contract =>
|
||||||
#{name => <<"C">>,
|
#{name => <<"C">>,
|
||||||
type_defs => [],
|
typedefs => [],
|
||||||
payable => true,
|
payable => true,
|
||||||
kind => contract_main,
|
kind => contract_main,
|
||||||
functions =>
|
functions =>
|
||||||
@@ -43,7 +43,7 @@ test_cases(2) ->
|
|||||||
MapACI = #{contract =>
|
MapACI = #{contract =>
|
||||||
#{name => <<"C">>, payable => false,
|
#{name => <<"C">>, payable => false,
|
||||||
kind => contract_main,
|
kind => contract_main,
|
||||||
type_defs =>
|
typedefs =>
|
||||||
[#{name => <<"allan">>,
|
[#{name => <<"allan">>,
|
||||||
typedef => <<"int">>,
|
typedef => <<"int">>,
|
||||||
vars => []}],
|
vars => []}],
|
||||||
@@ -76,7 +76,7 @@ test_cases(3) ->
|
|||||||
name => <<"C">>, payable => false, kind => contract_main,
|
name => <<"C">>, payable => false, kind => contract_main,
|
||||||
event => #{variant => [#{<<"SingleEventDefined">> => []}]},
|
event => #{variant => [#{<<"SingleEventDefined">> => []}]},
|
||||||
state => <<"unit">>,
|
state => <<"unit">>,
|
||||||
type_defs =>
|
typedefs =>
|
||||||
[#{name => <<"bert">>,
|
[#{name => <<"bert">>,
|
||||||
typedef =>
|
typedef =>
|
||||||
#{variant =>
|
#{variant =>
|
||||||
|
|||||||
@@ -203,6 +203,8 @@ compilable_contracts() ->
|
|||||||
"polymorphism_contract_interface_same_decl_multi_interface",
|
"polymorphism_contract_interface_same_decl_multi_interface",
|
||||||
"polymorphism_contract_interface_same_name_same_type",
|
"polymorphism_contract_interface_same_name_same_type",
|
||||||
"polymorphism_variance_switching_chain_create",
|
"polymorphism_variance_switching_chain_create",
|
||||||
|
"polymorphism_variance_switching_void_supertype",
|
||||||
|
"polymorphism_variance_switching_unify_with_interface_decls",
|
||||||
"missing_init_fun_state_unit",
|
"missing_init_fun_state_unit",
|
||||||
"complex_compare_leq",
|
"complex_compare_leq",
|
||||||
"complex_compare",
|
"complex_compare",
|
||||||
@@ -215,6 +217,8 @@ compilable_contracts() ->
|
|||||||
"polymorphic_map_keys",
|
"polymorphic_map_keys",
|
||||||
"unapplied_contract_call",
|
"unapplied_contract_call",
|
||||||
"unapplied_named_arg_builtin",
|
"unapplied_named_arg_builtin",
|
||||||
|
"resolve_field_constraint_by_arity",
|
||||||
|
"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.
|
||||||
].
|
].
|
||||||
|
|
||||||
@@ -731,10 +735,22 @@ failing_contracts() ->
|
|||||||
"Conflicting updates for field 'foo'">>])
|
"Conflicting updates for field 'foo'">>])
|
||||||
, ?TYPE_ERROR(factories_type_errors,
|
, ?TYPE_ERROR(factories_type_errors,
|
||||||
[<<?Pos(10,18)
|
[<<?Pos(10,18)
|
||||||
"Chain.clone requires `ref` named argument of contract type.">>,
|
"Chain.clone requires `ref` named argument of contract type.">>,
|
||||||
<<?Pos(11,18)
|
<<?Pos(11,18)
|
||||||
"Cannot unify `(gas : int, value : int, protected : bool) => if(protected, option(void), void)` and `(gas : int, value : int, protected : bool, int, bool) => 'b`\n"
|
"Cannot unify `(gas : int, value : int, protected : bool) => if(protected, option(void), void)` and `(gas : int, value : int, protected : bool, int, bool) => if(protected, option(void), void)`\n"
|
||||||
"when checking contract construction of type\n (gas : int, value : int, protected : bool) =>\n if(protected, option(void), void) (at line 11, column 18)\nagainst the expected type\n (gas : int, value : int, protected : bool, int, bool) => 'b">>,
|
"when checking contract construction of type\n"
|
||||||
|
" (gas : int, value : int, protected : bool) =>\n"
|
||||||
|
" if(protected, option(void), void) (at line 11, column 18)\n"
|
||||||
|
"against the expected type\n"
|
||||||
|
" (gas : int, value : int, protected : bool, int, bool) =>\n"
|
||||||
|
" if(protected, option(void), void)">>,
|
||||||
|
<<?Pos(11,18)
|
||||||
|
"Cannot unify `Bakoom` and `Kaboom`\n"
|
||||||
|
"when checking that contract construction of type\n"
|
||||||
|
" Bakoom\n"
|
||||||
|
"arising from resolution of variadic function `Chain.clone`\n"
|
||||||
|
"matches the expected type\n"
|
||||||
|
" Kaboom">>,
|
||||||
<<?Pos(12,37)
|
<<?Pos(12,37)
|
||||||
"Cannot unify `int` and `bool`\n"
|
"Cannot unify `int` and `bool`\n"
|
||||||
"when checking named argument `gas : int` against inferred type `bool`">>,
|
"when checking named argument `gas : int` against inferred type `bool`">>,
|
||||||
@@ -847,25 +863,25 @@ 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(4,20)
|
[<<?Pos(9,5)
|
||||||
"Unimplemented function `f` from the interface `I1` in the contract `I2`">>])
|
"Duplicate definitions of `f` at\n"
|
||||||
|
" - line 8, column 5\n"
|
||||||
|
" - line 9, column 5">>])
|
||||||
, ?TYPE_ERROR(polymorphism_contract_missing_implementation,
|
, ?TYPE_ERROR(polymorphism_contract_missing_implementation,
|
||||||
[<<?Pos(4,20)
|
[<<?Pos(4,20)
|
||||||
"Unimplemented function `f` from the interface `I1` in the contract `I2`">>
|
"Unimplemented function `f` from the interface `I1` in the contract `I2`">>
|
||||||
])
|
])
|
||||||
, ?TYPE_ERROR(polymorphism_contract_same_decl_multi_interface,
|
, ?TYPE_ERROR(polymorphism_contract_same_decl_multi_interface,
|
||||||
[<<?Pos(7,10)
|
[<<?Pos(7,10)
|
||||||
"Unimplemented function `f` from the interface `J` in the contract `C`">>
|
"Both interfaces `I` and `J` implemented by the contract `C` have a function called `f`">>
|
||||||
])
|
])
|
||||||
, ?TYPE_ERROR(polymorphism_contract_undefined_interface,
|
, ?TYPE_ERROR(polymorphism_contract_undefined_interface,
|
||||||
[<<?Pos(1,14)
|
[<<?Pos(1,14)
|
||||||
"Trying to implement or extend an undefined interface `I`">>
|
"Trying to implement or extend an undefined interface `I`">>
|
||||||
])
|
])
|
||||||
, ?TYPE_ERROR(polymorphism_contract_same_name_different_type_multi_interface,
|
, ?TYPE_ERROR(polymorphism_contract_same_name_different_type_multi_interface,
|
||||||
[<<?Pos(9,5)
|
[<<?Pos(7,10)
|
||||||
"Duplicate definitions of `f` at\n"
|
"Both interfaces `I` and `J` implemented by the contract `C` have a function called `f`">>
|
||||||
" - line 8, column 5\n"
|
|
||||||
" - line 9, column 5">>
|
|
||||||
])
|
])
|
||||||
, ?TYPE_ERROR(polymorphism_contract_interface_undefined_interface,
|
, ?TYPE_ERROR(polymorphism_contract_interface_undefined_interface,
|
||||||
[<<?Pos(1,24)
|
[<<?Pos(1,24)
|
||||||
@@ -1080,19 +1096,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)">>
|
||||||
])
|
])
|
||||||
@@ -1124,6 +1140,19 @@ failing_contracts() ->
|
|||||||
" `oracle(string, (int) => int)`\n"
|
" `oracle(string, (int) => int)`\n"
|
||||||
"The response type must not be higher-order (contain function types)">>
|
"The response type must not be higher-order (contain function types)">>
|
||||||
])
|
])
|
||||||
|
, ?TYPE_ERROR(var_args_unify_let,
|
||||||
|
[<<?Pos(3,9)
|
||||||
|
"Cannot infer types for variable argument list.\n"
|
||||||
|
"when checking the type of the pattern `x : 'a` against the expected type `(gas : int, value : int, protected : bool, ref : 'b, var_args) => 'b`">>
|
||||||
|
])
|
||||||
|
, ?TYPE_ERROR(var_args_unify_fun_call,
|
||||||
|
[<<?Pos(6,5)
|
||||||
|
"Cannot infer types for variable argument list.\n"
|
||||||
|
"when checking the application of\n"
|
||||||
|
" `g : (() => 'b) => 'b`\n"
|
||||||
|
"to arguments\n"
|
||||||
|
" `Chain.create : (value : int, var_args) => 'c`">>
|
||||||
|
])
|
||||||
].
|
].
|
||||||
|
|
||||||
validation_test_() ->
|
validation_test_() ->
|
||||||
|
|||||||
@@ -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 ],
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
+17
-17
@@ -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)
|
||||||
|
|||||||
@@ -1,17 +1,30 @@
|
|||||||
contract AENSUpdate =
|
include "Option.aes"
|
||||||
|
include "AENSCompat.aes"
|
||||||
|
contract interface OldAENSContract =
|
||||||
|
entrypoint set : (string, string, AENS.pointee) => unit
|
||||||
|
entrypoint lookup : (string, string) => AENS.pointee
|
||||||
|
|
||||||
|
main contract AENSUpdate =
|
||||||
stateful entrypoint update_name(owner : address, name : string) =
|
stateful entrypoint update_name(owner : address, name : string) =
|
||||||
let p1 : AENS.pointee = AENS.AccountPt(Call.caller)
|
let p1 : AENSv2.pointee = AENSv2.AccountPt(Call.caller)
|
||||||
let p2 : AENS.pointee = AENS.OraclePt(Call.caller)
|
let p2 : AENSv2.pointee = AENSv2.OraclePt(Call.caller)
|
||||||
let p3 : AENS.pointee = AENS.ContractPt(Call.caller)
|
let p3 : AENSv2.pointee = AENSv2.ContractPt(Call.caller)
|
||||||
let p4 : AENS.pointee = AENS.ChannelPt(Call.caller)
|
let p4 : AENSv2.pointee = AENSv2.ChannelPt(Call.caller)
|
||||||
AENS.update(owner, name, None, None,
|
let p5 : AENSv2.pointee = AENSv2.DataPt("any something will do")
|
||||||
Some({ ["account_pubkey"] = p1, ["oracle_pubkey"] = p2,
|
AENSv2.update(owner, name, None, None,
|
||||||
["contract_pubkey"] = p3, ["misc"] = p4 }))
|
Some({ ["account_pubkey"] = p1, ["oracle_pubkey"] = p2,
|
||||||
|
["contract_pubkey"] = p3, ["misc"] = p4, ["data"] = p5 }))
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
|||||||
@@ -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() = ()
|
||||||
|
|
||||||
|
|||||||
@@ -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() = ()
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
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)
|
||||||
|
(a bor b band c bxor a << bnot b >> a, k)
|
||||||
@@ -1,16 +1,7 @@
|
|||||||
|
|
||||||
contract interface Remote =
|
|
||||||
entrypoint up_to : (int) => list(int)
|
|
||||||
entrypoint sum : (list(int)) => int
|
|
||||||
entrypoint some_string : () => string
|
|
||||||
entrypoint pair : (int, string) => int * string
|
|
||||||
entrypoint squares : (int) => list(int * int)
|
|
||||||
entrypoint filter_some : (list(option(int))) => list(int)
|
|
||||||
entrypoint all_some : (list(option(int))) => option(list(int))
|
|
||||||
|
|
||||||
contract ComplexTypes =
|
contract ComplexTypes =
|
||||||
|
|
||||||
record state = { worker : Remote }
|
record state = { worker : ComplexTypes }
|
||||||
|
|
||||||
entrypoint init(worker) = {worker = worker}
|
entrypoint init(worker) = {worker = worker}
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,9 @@
|
|||||||
|
|
||||||
// Testing primitives for accessing the block chain environment
|
// Testing primitives for accessing the block chain environment
|
||||||
contract interface Interface =
|
|
||||||
entrypoint contract_address : () => address
|
|
||||||
entrypoint call_origin : () => address
|
|
||||||
entrypoint call_caller : () => address
|
|
||||||
entrypoint call_value : () => int
|
|
||||||
|
|
||||||
contract Environment =
|
contract Environment =
|
||||||
|
|
||||||
record state = {remote : Interface}
|
record state = {remote : Environment}
|
||||||
|
|
||||||
entrypoint init(remote) = {remote = remote}
|
entrypoint init(remote) = {remote = remote}
|
||||||
|
|
||||||
|
|||||||
@@ -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() = ()
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
payable contract interface SalesOffer =
|
||||||
|
entrypoint init : (address, address, int, int) => unit
|
||||||
|
|
||||||
|
payable contract Test : SalesOffer =
|
||||||
|
entrypoint init(_, _, _, _) = ()
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
payable contract interface SalesOffer =
|
||||||
|
entrypoint init : (address, address, int, int) => void
|
||||||
|
|
||||||
|
payable contract Test : SalesOffer =
|
||||||
|
entrypoint init(_ : address, _ : address, _ : int, _ : int) = ()
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
contract First =
|
||||||
|
entrypoint print_num(x) = 1 + x
|
||||||
|
|
||||||
|
contract Second =
|
||||||
|
entrypoint print_num() = 1
|
||||||
|
|
||||||
|
main contract Test =
|
||||||
|
entrypoint f(c) = c.print_num(1)
|
||||||
|
entrypoint g(c) = c.print_num()
|
||||||
@@ -1,7 +1,4 @@
|
|||||||
|
|
||||||
contract interface SpendContract =
|
|
||||||
entrypoint withdraw : (int) => int
|
|
||||||
|
|
||||||
contract SpendTest =
|
contract SpendTest =
|
||||||
|
|
||||||
stateful entrypoint spend(to, amount) =
|
stateful entrypoint spend(to, amount) =
|
||||||
|
|||||||
@@ -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) => _
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
contract C =
|
||||||
|
stateful function g(h) =
|
||||||
|
h()
|
||||||
|
|
||||||
|
stateful entrypoint f() =
|
||||||
|
g(Chain.create)
|
||||||
|
123
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
main contract C =
|
||||||
|
stateful entrypoint f() =
|
||||||
|
let x = Chain.clone
|
||||||
|
123
|
||||||
Reference in New Issue
Block a user