145 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
		
		
			
		
	
	
			145 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| 
								 | 
							
								// Copyright 2019 The go-ethereum Authors
							 | 
						||
| 
								 | 
							
								// This file is part of the go-ethereum library.
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// The go-ethereum library is free software: you can redistribute it and/or modify
							 | 
						||
| 
								 | 
							
								// it under the terms of the GNU Lesser General Public License as published by
							 | 
						||
| 
								 | 
							
								// the Free Software Foundation, either version 3 of the License, or
							 | 
						||
| 
								 | 
							
								// (at your option) any later version.
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// The go-ethereum library is distributed in the hope that it will be useful,
							 | 
						||
| 
								 | 
							
								// but WITHOUT ANY WARRANTY; without even the implied warranty of
							 | 
						||
| 
								 | 
							
								// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
							 | 
						||
| 
								 | 
							
								// GNU Lesser General Public License for more details.
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// You should have received a copy of the GNU Lesser General Public License
							 | 
						||
| 
								 | 
							
								// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Package compiler wraps the Solidity and Vyper compiler executables (solc; vyper).
							 | 
						||
| 
								 | 
							
								package compiler
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								import (
							 | 
						||
| 
								 | 
							
									"bytes"
							 | 
						||
| 
								 | 
							
									"encoding/json"
							 | 
						||
| 
								 | 
							
									"errors"
							 | 
						||
| 
								 | 
							
									"fmt"
							 | 
						||
| 
								 | 
							
									"os/exec"
							 | 
						||
| 
								 | 
							
									"strconv"
							 | 
						||
| 
								 | 
							
									"strings"
							 | 
						||
| 
								 | 
							
								)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Vyper contains information about the vyper compiler.
							 | 
						||
| 
								 | 
							
								type Vyper struct {
							 | 
						||
| 
								 | 
							
									Path, Version, FullVersion string
							 | 
						||
| 
								 | 
							
									Major, Minor, Patch        int
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (s *Vyper) makeArgs() []string {
							 | 
						||
| 
								 | 
							
									p := []string{
							 | 
						||
| 
								 | 
							
										"-f", "combined_json",
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									return p
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// VyperVersion runs vyper and parses its version output.
							 | 
						||
| 
								 | 
							
								func VyperVersion(vyper string) (*Vyper, error) {
							 | 
						||
| 
								 | 
							
									if vyper == "" {
							 | 
						||
| 
								 | 
							
										vyper = "vyper"
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									var out bytes.Buffer
							 | 
						||
| 
								 | 
							
									cmd := exec.Command(vyper, "--version")
							 | 
						||
| 
								 | 
							
									cmd.Stdout = &out
							 | 
						||
| 
								 | 
							
									err := cmd.Run()
							 | 
						||
| 
								 | 
							
									if err != nil {
							 | 
						||
| 
								 | 
							
										return nil, err
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									matches := versionRegexp.FindStringSubmatch(out.String())
							 | 
						||
| 
								 | 
							
									if len(matches) != 4 {
							 | 
						||
| 
								 | 
							
										return nil, fmt.Errorf("can't parse vyper version %q", out.String())
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									s := &Vyper{Path: cmd.Path, FullVersion: out.String(), Version: matches[0]}
							 | 
						||
| 
								 | 
							
									if s.Major, err = strconv.Atoi(matches[1]); err != nil {
							 | 
						||
| 
								 | 
							
										return nil, err
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									if s.Minor, err = strconv.Atoi(matches[2]); err != nil {
							 | 
						||
| 
								 | 
							
										return nil, err
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									if s.Patch, err = strconv.Atoi(matches[3]); err != nil {
							 | 
						||
| 
								 | 
							
										return nil, err
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									return s, nil
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// CompileVyper compiles all given Vyper source files.
							 | 
						||
| 
								 | 
							
								func CompileVyper(vyper string, sourcefiles ...string) (map[string]*Contract, error) {
							 | 
						||
| 
								 | 
							
									if len(sourcefiles) == 0 {
							 | 
						||
| 
								 | 
							
										return nil, errors.New("vyper: no source files")
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									source, err := slurpFiles(sourcefiles)
							 | 
						||
| 
								 | 
							
									if err != nil {
							 | 
						||
| 
								 | 
							
										return nil, err
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									s, err := VyperVersion(vyper)
							 | 
						||
| 
								 | 
							
									if err != nil {
							 | 
						||
| 
								 | 
							
										return nil, err
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									args := s.makeArgs()
							 | 
						||
| 
								 | 
							
									cmd := exec.Command(s.Path, append(args, sourcefiles...)...)
							 | 
						||
| 
								 | 
							
									return s.run(cmd, source)
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (s *Vyper) run(cmd *exec.Cmd, source string) (map[string]*Contract, error) {
							 | 
						||
| 
								 | 
							
									var stderr, stdout bytes.Buffer
							 | 
						||
| 
								 | 
							
									cmd.Stderr = &stderr
							 | 
						||
| 
								 | 
							
									cmd.Stdout = &stdout
							 | 
						||
| 
								 | 
							
									if err := cmd.Run(); err != nil {
							 | 
						||
| 
								 | 
							
										return nil, fmt.Errorf("vyper: %v\n%s", err, stderr.Bytes())
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									return ParseVyperJSON(stdout.Bytes(), source, s.Version, s.Version, strings.Join(s.makeArgs(), " "))
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// ParseVyperJSON takes the direct output of a vyper --f combined_json run and
							 | 
						||
| 
								 | 
							
								// parses it into a map of string contract name to Contract structs. The
							 | 
						||
| 
								 | 
							
								// provided source, language and compiler version, and compiler options are all
							 | 
						||
| 
								 | 
							
								// passed through into the Contract structs.
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// The vyper output is expected to contain ABI and source mapping.
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// Returns an error if the JSON is malformed or missing data, or if the JSON
							 | 
						||
| 
								 | 
							
								// embedded within the JSON is malformed.
							 | 
						||
| 
								 | 
							
								func ParseVyperJSON(combinedJSON []byte, source string, languageVersion string, compilerVersion string, compilerOptions string) (map[string]*Contract, error) {
							 | 
						||
| 
								 | 
							
									var output map[string]interface{}
							 | 
						||
| 
								 | 
							
									if err := json.Unmarshal(combinedJSON, &output); err != nil {
							 | 
						||
| 
								 | 
							
										return nil, err
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Compilation succeeded, assemble and return the contracts.
							 | 
						||
| 
								 | 
							
									contracts := make(map[string]*Contract)
							 | 
						||
| 
								 | 
							
									for name, info := range output {
							 | 
						||
| 
								 | 
							
										// Parse the individual compilation results.
							 | 
						||
| 
								 | 
							
										if name == "version" {
							 | 
						||
| 
								 | 
							
											continue
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										c := info.(map[string]interface{})
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										contracts[name] = &Contract{
							 | 
						||
| 
								 | 
							
											Code:        c["bytecode"].(string),
							 | 
						||
| 
								 | 
							
											RuntimeCode: c["bytecode_runtime"].(string),
							 | 
						||
| 
								 | 
							
											Info: ContractInfo{
							 | 
						||
| 
								 | 
							
												Source:          source,
							 | 
						||
| 
								 | 
							
												Language:        "Vyper",
							 | 
						||
| 
								 | 
							
												LanguageVersion: languageVersion,
							 | 
						||
| 
								 | 
							
												CompilerVersion: compilerVersion,
							 | 
						||
| 
								 | 
							
												CompilerOptions: compilerOptions,
							 | 
						||
| 
								 | 
							
												SrcMap:          c["source_map"],
							 | 
						||
| 
								 | 
							
												SrcMapRuntime:   "",
							 | 
						||
| 
								 | 
							
												AbiDefinition:   c["abi"],
							 | 
						||
| 
								 | 
							
												UserDoc:         "",
							 | 
						||
| 
								 | 
							
												DeveloperDoc:    "",
							 | 
						||
| 
								 | 
							
												Metadata:        "",
							 | 
						||
| 
								 | 
							
											},
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									return contracts, nil
							 | 
						||
| 
								 | 
							
								}
							 |