Skip to content
This repository has been archived by the owner on Dec 5, 2024. It is now read-only.

Add support for option --allow-paths #1010

Merged
merged 14 commits into from
Mar 9, 2018
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ethereumj-core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ dependencies {

compile "org.ethereum:leveldbjni-all:1.18.3" // native leveldb components

compile "org.ethereum:solcJ-all:0.4.8" // Solidity Compiler win/mac/linux binaries
compile "org.ethereum:solcJ-all:0.4.19" // Solidity Compiler win/mac/linux binaries

compile "com.cedarsoftware:java-util:1.8.0" // for deep equals
compile "org.javassist:javassist:3.15.0-GA"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,13 @@ public String getType() {
}
}

public enum StateMutabilityType {
pure,
view,
nonpayable,
payable
}

public enum FunctionType {
constructor,
function,
Expand All @@ -101,6 +108,7 @@ public static class Function {
public Param[] inputs = new Param[0];
public Param[] outputs = new Param[0];
public FunctionType type;
public StateMutabilityType stateMutability;

private Function() {}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,10 @@ public void onBlock(Block block, List<TransactionReceipt> receipts) {
throw new RuntimeException("Contract compilation failed:\n" + result.errors);
}
CompilationResult res = CompilationResult.parse(result.output);
if (res.contracts.isEmpty()) {
if (res.getContracts().isEmpty()) {
throw new RuntimeException("Compilation failed, no contracts returned:\n" + result.errors);
}
CompilationResult.ContractMetadata metadata = res.contracts.values().iterator().next();
CompilationResult.ContractMetadata metadata = res.getContracts().iterator().next();
if (metadata.bin == null || metadata.bin.isEmpty()) {
throw new RuntimeException("Compilation failed, no binary returned:\n" + result.errors);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -343,10 +343,10 @@ private CompilationResult.ContractMetadata compileContract() throws IOException
throw new RuntimeException("Contract compilation failed:\n" + result.errors);
}
CompilationResult res = CompilationResult.parse(result.output);
if (res.contracts.isEmpty()) {
if (res.getContracts().isEmpty()) {
throw new RuntimeException("Compilation failed, no contracts returned:\n" + result.errors);
}
CompilationResult.ContractMetadata metadata = res.contracts.values().iterator().next();
CompilationResult.ContractMetadata metadata = res.getContracts().iterator().next();
if (metadata.bin == null || metadata.bin.isEmpty()) {
throw new RuntimeException("Compilation failed, no binary returned:\n" + result.errors);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,26 @@
*/
package org.ethereum.solidity.compiler;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

@JsonIgnoreProperties(ignoreUnknown = true)
public class CompilationResult {

public Map<String, ContractMetadata> contracts;
public String version;
@JsonProperty("contracts") private Map<String, ContractMetadata> contracts;
@JsonProperty("version") public String version;

public static CompilationResult parse(String rawJson) throws IOException {
@JsonIgnore public static CompilationResult parse(String rawJson) throws IOException {
if(rawJson == null || rawJson.isEmpty()){
CompilationResult empty = new CompilationResult();
empty.contracts = Collections.emptyMap();
Expand All @@ -42,6 +48,52 @@ public static CompilationResult parse(String rawJson) throws IOException {
}
}

@JsonIgnore public Path getContractPath() {
if (contracts.size() > 1) {
throw new UnsupportedOperationException("Source contains more than 1 contact. Please specify the contract name. Available keys (" + getContractKeys() + ").");
} else {
String key = contracts.keySet().iterator().next();
return Paths.get(key.substring(0, key.lastIndexOf(':')));
}
}

@JsonIgnore public String getContractName() {
if (contracts.size() > 1) {
throw new UnsupportedOperationException("Source contains more than 1 contact. Please specify the contract name. Available keys (" + getContractKeys() + ").");
} else {
String key = contracts.keySet().iterator().next();
return key.substring(key.lastIndexOf(':') + 1);
}
}

@JsonIgnore public ContractMetadata getContract(String contractName) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add javadoc to getters, especially to this one. There should be mentioned, that if there are more than 1 contract with the same name, only first will be returned, so you should use {@link getContract(Path contractPath, String contractName)} for this cases.

if (contractName == null && contracts.size() == 1) {
return contracts.values().iterator().next();
} else if (contractName == null || contractName.isEmpty()) {
throw new UnsupportedOperationException("Source contains more than 1 contact. Please specify the contract name. Available keys (" + getContractKeys() + ").");
}
for (Map.Entry<String, ContractMetadata> entry : contracts.entrySet()) {
String key = entry.getKey();
String name = key.substring(key.lastIndexOf(':') + 1);
if (contractName.equals(name)) {
return entry.getValue();
}
}
throw new UnsupportedOperationException("Source contains more than 1 contact. Please specify a valid contract name. Available keys (" + getContractKeys() + ").");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There should be something with "not found"
Contract %s was not found in compilation result or something like this.

}

@JsonIgnore public ContractMetadata getContract(Path contractPath, String contractName) {
return contracts.get(contractPath.toAbsolutePath().toString() + ':' + contractName);
}

@JsonIgnore public List<ContractMetadata> getContracts() {
return new ArrayList<>(contracts.values());
}

@JsonIgnore public List<String> getContractKeys() {
return new ArrayList<>(contracts.keySet());
}

@JsonIgnoreProperties(ignoreUnknown = true)
public static class ContractMetadata {
public String abi;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,24 @@
*/
package org.ethereum.solidity.compiler;

import com.google.common.base.Joiner;
import org.ethereum.config.SystemProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.*;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Serializable;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import static java.util.stream.Collectors.toList;

@Component
public class SolidityCompiler {

Expand All @@ -38,38 +47,114 @@ public SolidityCompiler(SystemProperties config) {
solc = new Solc(config);
}

public static Result compile(File sourceDirectory, boolean combinedJson, Options... options) throws IOException {
public static Result compile(File sourceDirectory, boolean combinedJson, Option... options) throws IOException {
return getInstance().compileSrc(sourceDirectory, false, combinedJson, options);
}

public enum Options {
/**
* This class is mainly here for backwards compatibility; however we are now reusing it making it the solely public
* interface listing all the supported options.
*/
public static final class Options {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you, please, add comment here, that it's here for backward compatibility

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do.

public static final OutputOption AST = OutputOption.AST;
public static final OutputOption BIN = OutputOption.BIN;
public static final OutputOption INTERFACE = OutputOption.INTERFACE;
public static final OutputOption ABI = OutputOption.ABI;
public static final OutputOption METADATA = OutputOption.METADATA;
public static final OutputOption ASTJSON = OutputOption.ASTJSON;

private static final NameOnlyOption OPTIMIZE = NameOnlyOption.OPTIMIZE;
private static final NameOnlyOption VERSION = NameOnlyOption.VERSION;

private static class CombinedJson extends ListOption {
private CombinedJson(List values) {
super("combined-json", values);
}
}
public static class AllowPaths extends ListOption {
public AllowPaths(List values) {
super("allow-paths", values);
}
}
}

public interface Option extends Serializable {
String getValue();
String getName();
}

private static class ListOption implements Option {
private String name;
private List values;

private ListOption(String name, List values) {
this.name = name;
this.values = values;
}

@Override public String getValue() {
StringBuilder result = new StringBuilder();
for (Object value : values) {
if (OutputOption.class.isAssignableFrom(value.getClass())) {
result.append((result.length() == 0) ? ((OutputOption) value).getName() : ',' + ((OutputOption) value).getName());
} else if (Path.class.isAssignableFrom(value.getClass())) {
result.append((result.length() == 0) ? ((Path) value).toAbsolutePath().toString() : ',' + ((Path) value).toAbsolutePath().toString());
} else if (File.class.isAssignableFrom(value.getClass())) {
result.append((result.length() == 0) ? ((File) value).getAbsolutePath() : ',' + ((File) value).getAbsolutePath());
} else if (String.class.isAssignableFrom(value.getClass())) {
result.append((result.length() == 0) ? value : "," + value);
} else {
throw new UnsupportedOperationException("Unexpected type, value '" + value + "' cannot be retrieved.");
}
}
return result.toString();
}
@Override public String getName() { return name; }
@Override public String toString() { return name; }
}

private enum NameOnlyOption implements Option {
OPTIMIZE("optimize"),
VERSION("version");

private String name;

NameOnlyOption(String name) {
this.name = name;
}

@Override public String getValue() { return ""; }
@Override public String getName() { return name; }
@Override public String toString() {
return name;
}
}

private enum OutputOption implements Option {
AST("ast"),
BIN("bin"),
INTERFACE("interface"),
ABI("abi"),
METADATA("metadata"),
METADATA("metadata"),
ASTJSON("ast-json");

private String name;

Options(String name) {
OutputOption(String name) {
this.name = name;
}

public String getName() {
return name;
}

@Override
public String toString() {
@Override public String getValue() { return ""; }
@Override public String getName() { return name; }
@Override public String toString() {
return name;
}
}

public static class Result {
public String errors;
public String output;
private boolean success = false;
private boolean success;

public Result(String errors, String output, boolean success) {
this.errors = errors;
Expand Down Expand Up @@ -125,11 +210,11 @@ public void run() {
}
}

public static Result compile(byte[] source, boolean combinedJson, Options... options) throws IOException {
public static Result compile(byte[] source, boolean combinedJson, Option... options) throws IOException {
return getInstance().compileSrc(source, false, combinedJson, options);
}

public Result compileSrc(File source, boolean optimize, boolean combinedJson, Options... options) throws IOException {
public Result compileSrc(File source, boolean optimize, boolean combinedJson, Option... options) throws IOException {
List<String> commandParts = prepareCommandOptions(optimize, combinedJson, options);

commandParts.add(source.getAbsolutePath());
Expand All @@ -156,24 +241,33 @@ public Result compileSrc(File source, boolean optimize, boolean combinedJson, Op
return new Result(error.getContent(), output.getContent(), success);
}

private List<String> prepareCommandOptions(boolean optimize, boolean combinedJson, Options[] options) throws IOException {
private List<String> prepareCommandOptions(boolean optimize, boolean combinedJson, Option... options) throws IOException {
List<String> commandParts = new ArrayList<>();
commandParts.add(solc.getExecutable().getCanonicalPath());
if (optimize) {
commandParts.add("--optimize");
commandParts.add("--" + Options.OPTIMIZE.getName());
}
if (combinedJson) {
commandParts.add("--combined-json");
commandParts.add(Joiner.on(',').join(options));
Option combinedJsonOption = new Options.CombinedJson(getElementsOf(OutputOption.class, options));
commandParts.add("--" + combinedJsonOption.getName());
commandParts.add(combinedJsonOption.getValue());
} else {
for (Options option : options) {
for (Option option : getElementsOf(OutputOption.class, options)) {
commandParts.add("--" + option.getName());
}
}
for (Option option : getElementsOf(ListOption.class, options)) {
commandParts.add("--" + option.getName());
commandParts.add(option.getValue());
}
return commandParts;
}

public Result compileSrc(byte[] source, boolean optimize, boolean combinedJson, Options... options) throws IOException {
private static <T> List<T> getElementsOf(Class<T> clazz, Option... options) {
return Arrays.stream(options).filter(clazz::isInstance).map(clazz::cast).collect(toList());
}

public Result compileSrc(byte[] source, boolean optimize, boolean combinedJson, Option... options) throws IOException {
List<String> commandParts = prepareCommandOptions(optimize, combinedJson, options);

ProcessBuilder processBuilder = new ProcessBuilder(commandParts)
Expand Down Expand Up @@ -205,7 +299,7 @@ public Result compileSrc(byte[] source, boolean optimize, boolean combinedJson,
public static String runGetVersionOutput() throws IOException {
List<String> commandParts = new ArrayList<>();
commandParts.add(getInstance().solc.getExecutable().getCanonicalPath());
commandParts.add("--version");
commandParts.add("--" + Options.VERSION.getName());

ProcessBuilder processBuilder = new ProcessBuilder(commandParts)
.directory(getInstance().solc.getExecutable().getParentFile());
Expand All @@ -231,12 +325,10 @@ public static String runGetVersionOutput() throws IOException {
throw new RuntimeException("Problem getting solc version: " + error.getContent());
}



public static SolidityCompiler getInstance() {
if (INSTANCE == null) {
INSTANCE = new SolidityCompiler(SystemProperties.getDefault());
}
return INSTANCE;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -381,11 +381,7 @@ private SolidityContractImpl createContract(String soliditySrc, String contractN
private SolidityContractImpl createContractFromJson(String contractName, String json) throws IOException {
CompilationResult result = CompilationResult.parse(json);
if (contractName == null) {
if (result.contracts.size() > 1) {
throw new RuntimeException("Source contains more than 1 contact (" + result.contracts.keySet() + "). Please specify the contract name");
} else {
contractName = result.contracts.keySet().iterator().next();
}
contractName = result.getContractName();
}

return createContract(contractName, result);
Expand All @@ -397,10 +393,10 @@ private SolidityContractImpl createContractFromJson(String contractName, String
* @return
*/
private SolidityContractImpl createContract(String contractName, CompilationResult result) {
ContractMetadata cMetaData = result.contracts.get(contractName);
ContractMetadata cMetaData = result.getContract(contractName);
SolidityContractImpl contract = createContract(cMetaData);

for (CompilationResult.ContractMetadata metadata : result.contracts.values()) {
for (CompilationResult.ContractMetadata metadata : result.getContracts()) {
contract.addRelatedContract(metadata.abi);
}
return contract;
Expand Down
Loading