Skip to content

Commit

Permalink
Initial draft solc functionality impl (no binaries so far)
Browse files Browse the repository at this point in the history
  • Loading branch information
Nashatyrev committed Mar 3, 2016
1 parent ecb504b commit dd4158b
Show file tree
Hide file tree
Showing 7 changed files with 416 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package org.ethereum.solidity.compiler;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
import org.codehaus.jackson.annotate.JsonProperty;

import java.io.IOException;
import java.util.Map;

@JsonIgnoreProperties(ignoreUnknown = true)
public class CompilationResult {

public Map<String, ContractMetadata> contracts;
public String version;

public static CompilationResult parse(String rawJson) throws IOException {
return new ObjectMapper().readValue(rawJson, CompilationResult.class);
}

@JsonIgnoreProperties(ignoreUnknown = true)
public static class ContractMetadata {
public String abi;
public String bin;
public String solInterface;

public String getInterface() {
return solInterface;
}

public void setInterface(String solInterface) {
this.solInterface = solInterface;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package org.ethereum.solidity.compiler;

public class ContractException extends RuntimeException {

public ContractException(String message) {
super(message);
}

public static ContractException permissionError(String msg, Object... args) {
return error("contract permission error", msg, args);
}

public static ContractException compilationError(String msg, Object... args) {
return error("contract compilation error", msg, args);
}

public static ContractException validationError(String msg, Object... args) {
return error("contract validation error", msg, args);
}

public static ContractException assembleError(String msg, Object... args) {
return error("contract assemble error", msg, args);
}

private static ContractException error(String title, String message, Object... args) {
return new ContractException(title + ": " + String.format(message, args));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package org.ethereum.solidity.compiler;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.Scanner;

/**
* Created by Anton Nashatyrev on 03.03.2016.
*/
public class Solc {

public static final Solc INSTANCE = new Solc();

private File solc = null;

private Solc() {
try {
init();
} catch (IOException e) {
throw new RuntimeException(e);
}
}

private void init() throws IOException {
File tmpDir = new File(System.getProperty("java.io.tmpdir"));

InputStream is = getClass().getResourceAsStream("/bin/" + getOS() + "/file.list");
Scanner scanner = new Scanner(is);
while (scanner.hasNext()) {
String s = scanner.next();
File targetFile = new File(tmpDir, s);
if (!targetFile.canRead()) {
InputStream fis = getClass().getResourceAsStream("/bin/" + getOS() + "/" + s);
Files.copy(fis, targetFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
}
if (solc == null) {
// first file in the list denotes executable
solc = targetFile;
solc.setExecutable(true);
}
targetFile.deleteOnExit();
}

}

private static String getOS() {
String osName = System.getProperty("os.name").toLowerCase();
if (osName.contains("win")) {
return "win";
} else if (osName.contains("linux")) {
return "linux";
} else if (osName.contains("mac")) {
return "mac";
} else {
throw new RuntimeException("Can't find solc compiler for unrecognized OS: " + osName);
}
}

public File getExecutable() {
return solc;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package org.ethereum.solidity.compiler;

import com.google.common.base.Joiner;
import com.google.common.base.Strings;

import java.io.*;
import java.util.ArrayList;
import java.util.List;

import static org.apache.commons.lang3.StringUtils.isNotBlank;

public class SolidityCompiler {

public enum Options {
AST("ast"),
BIN("bin"),
INTERFACE("interface"),
ABI("abi");

private String name;

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

public String getName() {
return name;
}

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

public static class Result {
public String errors;
public String output;

public Result(String errors, String output) {
this.errors = errors;
this.output = output;
}

public boolean isFailed() {
return isNotBlank(errors);
}
}

private static class ParallelReader extends Thread {

private InputStream stream;
private StringBuilder content = new StringBuilder();

ParallelReader(InputStream stream) {
this.stream = stream;
}

public String getContent() {
return content.toString();
}

public void run() {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(stream));) {
String line;
while ((line = reader.readLine()) != null) {
content.append(line).append("\n");
}
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
}

private static String readStream(InputStream stream) throws IOException {
StringBuilder result = new StringBuilder();

try (BufferedReader reader =new BufferedReader(new InputStreamReader(stream))) {
String line;
while ((line = reader.readLine()) != null) {
result.append(line).append("\n");
}
}

return result.toString();
}

public static Result compile(byte[] source, boolean combinedJson, Options... options) throws IOException {
List<String> commandParts = new ArrayList<>();
commandParts.add(Solc.INSTANCE.getExecutable().getCanonicalPath());
if (combinedJson) {
commandParts.add("--combined-json");
commandParts.add(Joiner.on(',').join(options));
} else {
for (Options option : options) {
commandParts.add("--" + option.getName());
}
}

Process process = new ProcessBuilder(commandParts)
.directory(Solc.INSTANCE.getExecutable().getParentFile())
.start();

try (BufferedOutputStream stream = new BufferedOutputStream(process.getOutputStream())) {
stream.write(source);
}

ParallelReader error = new ParallelReader(process.getErrorStream());
ParallelReader output = new ParallelReader(process.getInputStream());
error.start();
output.start();

try {
process.waitFor();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}

return new Result(error.getContent(), output.getContent());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package org.ethereum.solidity.compiler;


import java.io.File;
import java.io.IOException;
import java.util.*;

import static java.lang.String.format;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
import static org.apache.commons.collections4.CollectionUtils.disjunction;
import static org.apache.commons.collections4.CollectionUtils.isNotEmpty;
import static org.apache.commons.lang3.StringUtils.substringsBetween;
import static org.ethereum.solidity.compiler.ContractException.assembleError;

public class SourceArtifact {

private String name;
private List<String> dependencies;
private String source;

private final Set<SourceArtifact> injectedDependencies = new HashSet<>();
private final Set<SourceArtifact> dependentArtifacts = new HashSet<>();

public SourceArtifact(String name, String source) {
this.name = name;
this.dependencies = extractDependencies(source);
this.source = source.replaceAll("import\\s\"\\.*?\\.sol\";", "");
}

public SourceArtifact(File f) {

}

private static List<String> extractDependencies(String source) {
String[] deps = substringsBetween(source, "import \"", "\";");
return deps == null ? Collections.<String>emptyList() : asList(deps);
}

// public SourceArtifact(MultipartFile srcFile) throws IOException {
// this(srcFile.getOriginalFilename(), new String(srcFile.getBytes(), "UTF-8"));
// }

public void injectDependency(SourceArtifact srcArtifact) {
injectedDependencies.add(srcArtifact);
srcArtifact.addDependentArtifact(this);
}

private void addDependentArtifact(SourceArtifact srcArtifact) {
dependentArtifacts.add(srcArtifact);
}

public boolean hasDependentArtifacts() {
return !dependentArtifacts.isEmpty();
}

private Collection<String> getUnresolvedDependencies() {
Set<String> ret = new HashSet<>();
for (SourceArtifact injectedDependency : injectedDependencies) {
ret.add(injectedDependency.getName());
}

return disjunction(dependencies, ret);
}

public String plainSource() {
Collection<String> unresolvedDeps = getUnresolvedDependencies();
if (isNotEmpty(unresolvedDeps)) {
throw assembleError("Followed dependencies aren't resolved: %s", unresolvedDeps);
}

String result = this.source;
for (SourceArtifact dependencyArtifact : injectedDependencies) {
String importDefinition = format("import \"%s\";", dependencyArtifact.getName());
String dependencySrc = format("// %s\n%s", importDefinition, dependencyArtifact.plainSource());

result = result.replace(importDefinition, dependencySrc);
}

return result;
}

public String getName() {
return name;
}

public List<String> getDependencies() {
return dependencies;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package org.ethereum.solidity.compiler;

import java.io.File;
import java.util.HashMap;
import java.util.Map;

import static org.ethereum.solidity.compiler.ContractException.assembleError;

public class Sources {

private final Map<String, SourceArtifact> artifacts = new HashMap<>();
private String targetArtifact;

public Sources(File[] files) {
for (File file : files) {
artifacts.put(file.getName(), new SourceArtifact(file));
}
}

public void resolveDependencies() {
for (String srcName : artifacts.keySet()) {
SourceArtifact src = artifacts.get(srcName);
for (String dep : src.getDependencies()) {
SourceArtifact depArtifact = artifacts.get(dep);
if (depArtifact == null) {
throw assembleError("can't resolve dependency: dependency '%s' not found.", dep);
}
src.injectDependency(depArtifact);
};
}

for (SourceArtifact artifact : artifacts.values()) {
if (!artifact.hasDependentArtifacts()) {
targetArtifact = artifact.getName();
}
}
}

public String plainSource() {
return artifacts.get(targetArtifact).plainSource();
}
}
Loading

0 comments on commit dd4158b

Please sign in to comment.