-
Notifications
You must be signed in to change notification settings - Fork 80
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for libraries/macros #153
Comments
I'm thinking how this would work in practice with syntax checking pugins, would we call the function on the library like this int result = Math.muldiv(x, y, z); If we want to use the function name directly, it might be a good idea to do explicit imports to allow for highlighting. import { muldiv } from "Math.cash";
contract Example() {
function test(int x, int y, int z) {
int result = muldiv(x, y, z);
}
} However, code completion would work better with the reversed order: from "Math.cash" import { muldiv }; |
@mr-zwets and I just had a call about this, some of the main points:
Steps to get there / checklist:
Extensions:
|
We need to consider how we resolve the dependency graph for contracts / libraries that have (semi-)complex dependency graphs. We'll need to think about this. |
In certain cases, contracts (or collections of contracts) reuse pieces of code. For readability and reusability of complex operations it would be good to allow defining libraries/macros that live outside of a contract and can be used by different contracts.
Under the hood, the compiler would then replace the library function call statement with its bytecode.
One option would to use macros (aka string replacements). E.g.:
The benefit of this is that it results in the most control over performance / bytesize for the developer. But it is also very easy for this to result in worse readability, e.g. using
DO_STUFF()
would cause confusion:So it likely makes more sense to go with a properly typed library system, even at the cost of slightly larger / less efficient contracts (note: size can probably be brought down by optimisations in the future if needed). We also think it's likely that the opcode limit will be increased at some point in the future.
So what does a library look like? For example:
A library is a collection of functions, that get compiled individually. The function has parameters and can potentially return one value. This can be extended to multiple values in the future (by "upgrading" the tuple type to allow for larger tuples).
From the consuming contract's perspective:
When the library is called in a contract, the compiler treats it as a built in function call (e.g.
abs()
). In other words, it puts the args on top of the stack and replacesabs()
withOP_ABS
, exceptOP_ABS
will be a (much) larger piece of bytecode, with more than one opcode, e.g.OP_SWAP OP_MUL OP_DIV
. The function-to-bytecode mapping is retrieved from the compiled "library artifact" (see below).From the library's perspective:
Every function in a library is compiled independently. Compiling a library function is similar to compiling a contract with a single function, but there are a few notable differences:
OP_VERIFY
, because we only need to do that at the end of a contract execution, not some function callreturn
statement that preserves the top stack value and cleans the rest of the stack (known to the function)We also need to add import functionality, allowing importing libraries into contracts or into other libraries. For simplicity we can stick to 1 library/contract per file.
The text was updated successfully, but these errors were encountered: