package com.oracle.truffle.llvm.parser.metadata.debuginfo;
import com.oracle.truffle.api.TruffleFile;
import com.oracle.truffle.api.TruffleLanguage.Env;
import com.oracle.truffle.api.source.Source;
import com.oracle.truffle.api.source.Source.SourceBuilder;
import com.oracle.truffle.api.source.SourceSection;
import com.oracle.truffle.llvm.parser.metadata.MDBaseNode;
import com.oracle.truffle.llvm.parser.metadata.MDBasicType;
import com.oracle.truffle.llvm.parser.metadata.MDCommonBlock;
import com.oracle.truffle.llvm.parser.metadata.MDCompileUnit;
import com.oracle.truffle.llvm.parser.metadata.MDCompositeType;
import com.oracle.truffle.llvm.parser.metadata.MDDerivedType;
import com.oracle.truffle.llvm.parser.metadata.MDFile;
import com.oracle.truffle.llvm.parser.metadata.MDGlobalVariable;
import com.oracle.truffle.llvm.parser.metadata.MDGlobalVariableExpression;
import com.oracle.truffle.llvm.parser.metadata.MDLabel;
import com.oracle.truffle.llvm.parser.metadata.MDLexicalBlock;
import com.oracle.truffle.llvm.parser.metadata.MDLexicalBlockFile;
import com.oracle.truffle.llvm.parser.metadata.MDLocalVariable;
import com.oracle.truffle.llvm.parser.metadata.MDLocation;
import com.oracle.truffle.llvm.parser.metadata.MDMacroFile;
import com.oracle.truffle.llvm.parser.metadata.MDModule;
import com.oracle.truffle.llvm.parser.metadata.MDNamespace;
import com.oracle.truffle.llvm.parser.metadata.MDString;
import com.oracle.truffle.llvm.parser.metadata.MDSubprogram;
import com.oracle.truffle.llvm.parser.metadata.MDVoidNode;
import com.oracle.truffle.llvm.parser.metadata.MetadataValueList;
import com.oracle.truffle.llvm.parser.metadata.MetadataVisitor;
import com.oracle.truffle.llvm.runtime.LLVMLanguage;
import com.oracle.truffle.llvm.runtime.debug.scope.LLVMSourceLocation;
import com.oracle.truffle.llvm.runtime.debug.scope.LLVMSourceLocation.LazySourceSection;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
final class DIScopeBuilder {
private static final String MIMETYPE_PLAINTEXT = "text/plain";
private static final String MIMETYPE_UNAVAILABLE = "sulong/unavailable";
private static final String STDIN_FILENAME = "-";
private static final String STDIN_SOURCE_TEXT = "STDIN";
static String getMimeType(String path) {
if (path == null) {
return MIMETYPE_PLAINTEXT;
}
int extStartIndex = path.lastIndexOf('.') + 1;
if (extStartIndex <= 0 || extStartIndex >= path.length()) {
return MIMETYPE_PLAINTEXT;
}
switch (path.substring(extStartIndex)) {
case "c":
case "C":
case "cpp":
return "text/x-c";
case "h":
return "text/x-h";
case "f":
case "f90":
case "for":
return "text/x-fortran";
case "rs":
return "text/x-rust";
case "ll":
return "text/x-llvmir";
default:
return MIMETYPE_PLAINTEXT;
}
}
private TruffleFile[] getSourceFiles(MDFile file) {
if (sourceFileCache.containsKey(file)) {
return sourceFileCache.get(file);
}
final Env env = LLVMLanguage.getContext().getEnv();
String name = MDString.getIfInstance(file.getMDFile());
TruffleFile[] sourceFiles;
if (STDIN_FILENAME.equals(name)) {
sourceFiles = null;
} else {
TruffleFile simple = env.getInternalTruffleFile(name);
if (simple.isAbsolute()) {
sourceFiles = new TruffleFile[]{simple};
} else {
String directoryName = MDString.getIfInstance(file.getMDDirectory());
if (directoryName != null) {
TruffleFile qualifiedFile = env.getInternalTruffleFile(directoryName + env.getFileNameSeparator() + name);
sourceFiles = new TruffleFile[]{qualifiedFile, simple};
} else {
sourceFiles = new TruffleFile[]{simple};
}
}
}
sourceFileCache.put(file, sourceFiles);
return sourceFiles;
}
private final HashMap<MDBaseNode, LLVMSourceLocation> globalCache;
private final HashMap<MDBaseNode, LLVMSourceLocation> localCache;
private final HashMap<MDFile, TruffleFile[]> sourceFileCache;
private final HashMap<String, Source> sources;
private final MetadataValueList metadata;
private final FileExtractor ;
DIScopeBuilder(MetadataValueList metadata) {
this.metadata = metadata;
this.fileExtractor = new FileExtractor();
this.globalCache = new HashMap<>();
this.localCache = new HashMap<>();
this.sourceFileCache = new HashMap<>();
this.sources = new HashMap<>();
}
private static boolean isLocalScope(LLVMSourceLocation location) {
switch (location.getKind()) {
case LINE:
case LOCAL:
return true;
default:
return false;
}
}
LLVMSourceLocation buildLocation(MDBaseNode md) {
if (globalCache.containsKey(md)) {
return globalCache.get(md);
} else if (localCache.containsKey(md)) {
return localCache.get(md);
}
final Builder builder = new Builder();
md.accept(builder);
final LLVMSourceLocation location = builder.build();
if (isLocalScope(location)) {
localCache.put(md, location);
} else {
globalCache.put(md, location);
}
return location;
}
void clearLocalScopes() {
localCache.clear();
}
void importScope(MDBaseNode node, LLVMSourceLocation importedScope) {
globalCache.put(node, importedScope);
}
private static final class LazySourceSectionImpl extends LazySourceSection {
private final TruffleFile[] sourceFiles;
private final String path;
private final int line;
private final int column;
private final HashMap<String, Source> sources;
LazySourceSectionImpl(HashMap<String, Source> sources, TruffleFile[] sourceFiles, String path, int line, int column) {
this.sources = sources;
this.sourceFiles = sourceFiles;
this.path = path;
this.line = line;
this.column = column;
}
@Override
public SourceSection get() {
Source source = asSource(sources, sourceFiles, path);
if (source == null) {
return null;
}
SourceSection section;
try {
if (MIMETYPE_UNAVAILABLE.equals(source.getMimeType())) {
section = source.createUnavailableSection();
} else if (line < 0) {
section = source.createSection(0, source.getLength());
} else if (line == 0) {
section = source.createSection(1);
} else if (column <= 0) {
section = source.createSection(line);
} else {
section = source.createSection(line, column, line, column);
}
} catch (IllegalArgumentException ignored) {
section = null;
}
return section;
}
@Override
public String getPath() {
return path;
}
@Override
public int getLine() {
return line;
}
@Override
public int getColumn() {
return column;
}
}
private final class Builder implements MetadataVisitor {
LLVMSourceLocation loc;
private LLVMSourceLocation parent;
private LLVMSourceLocation.Kind kind;
private String name;
private LazySourceSectionImpl sourceSection;
private MDFile file;
private long line;
private long col;
private Builder() {
parent = null;
kind = LLVMSourceLocation.Kind.UNKNOWN;
name = null;
sourceSection = null;
file = null;
line = -1;
col = -1;
}
public LLVMSourceLocation build() {
if (loc == null) {
sourceSection = buildSection(file, line, col);
loc = LLVMSourceLocation.create(parent, kind, name, sourceSection, null);
}
return loc;
}
@Override
public void visit(MDLocation md) {
parent = buildLocation(md.getScope());
kind = LLVMSourceLocation.Kind.LINE;
file = fileExtractor.extractFile(md);
line = md.getLine();
col = md.getColumn();
}
@Override
public void visit(MDLexicalBlock md) {
if (md.getScope() != MDVoidNode.INSTANCE) {
parent = buildLocation(md.getScope());
} else {
parent = buildLocation(md.getFile());
}
kind = LLVMSourceLocation.Kind.BLOCK;
file = fileExtractor.extractFile(md);
line = md.getLine();
col = md.getColumn();
}
@Override
public void visit(MDLexicalBlockFile md) {
if (md.getScope() != MDVoidNode.INSTANCE) {
parent = buildLocation(md.getScope());
} else {
parent = buildLocation(md.getFile());
}
kind = LLVMSourceLocation.Kind.BLOCK;
file = fileExtractor.extractFile(md);
}
@Override
public void visit(MDSubprogram md) {
if (md.getScope() != MDVoidNode.INSTANCE) {
parent = buildLocation(md.getScope());
} else {
parent = buildLocation(md.getCompileUnit());
}
kind = LLVMSourceLocation.Kind.FUNCTION;
file = fileExtractor.extractFile(md);
line = md.getLine();
name = MDNameExtractor.getName(md.getName());
final LLVMSourceLocation compileUnit = buildLocation(md.getCompileUnit());
sourceSection = buildSection(file, line, col);
loc = LLVMSourceLocation.create(parent, kind, name, sourceSection, compileUnit);
}
@Override
public void visit(MDNamespace md) {
parent = buildLocation(md.getScope());
kind = LLVMSourceLocation.Kind.NAMESPACE;
name = MDNameExtractor.getName(md.getName());
}
@Override
public void visit(MDCompileUnit md) {
kind = LLVMSourceLocation.Kind.COMPILEUNIT;
}
@Override
public void visit(MDFile md) {
kind = LLVMSourceLocation.Kind.FILE;
file = fileExtractor.extractFile(md);
}
@Override
public void visit(MDModule md) {
parent = buildLocation(md.getScope());
kind = LLVMSourceLocation.Kind.MODULE;
name = MDNameExtractor.getName(md.getName());
}
@Override
public void visit(MDCommonBlock md) {
parent = buildLocation(md.getScope());
kind = LLVMSourceLocation.Kind.COMMON_BLOCK;
name = MDNameExtractor.getName(md.getName());
file = fileExtractor.extractFile(md);
line = md.getLine();
}
@Override
public void visit(MDBasicType md) {
kind = LLVMSourceLocation.Kind.TYPE;
file = fileExtractor.extractFile(md);
name = MDNameExtractor.getName(md.getName());
}
@Override
public void visit(MDCompositeType md) {
parent = buildLocation(md.getScope());
kind = LLVMSourceLocation.Kind.TYPE;
file = fileExtractor.extractFile(md);
name = MDNameExtractor.getName(md.getName());
line = md.getLine();
}
@Override
public void visit(MDDerivedType md) {
parent = buildLocation(md.getScope());
kind = LLVMSourceLocation.Kind.TYPE;
file = fileExtractor.extractFile(md);
name = MDNameExtractor.getName(md.getName());
line = md.getLine();
}
@Override
public void visit(MDGlobalVariable md) {
if (md.getScope() != MDVoidNode.INSTANCE) {
parent = buildLocation(md.getScope());
} else {
parent = buildLocation(md.getCompileUnit());
}
kind = LLVMSourceLocation.Kind.GLOBAL;
file = fileExtractor.extractFile(md);
name = MDNameExtractor.getName(md.getName());
line = md.getLine();
}
@Override
public void visit(MDLocalVariable md) {
parent = buildLocation(md.getScope());
kind = LLVMSourceLocation.Kind.LOCAL;
file = fileExtractor.extractFile(md);
name = MDNameExtractor.getName(md.getName());
line = md.getLine();
}
@Override
public void visit(MDString md) {
final MDCompositeType actualType = metadata.identifyType(md.getString());
loc = buildLocation(actualType);
globalCache.put(md, loc);
}
@Override
public void visit(MDGlobalVariableExpression md) {
final MDBaseNode variable = md.getGlobalVariable();
loc = buildLocation(variable);
globalCache.put(md, loc);
}
@Override
public void visit(MDLabel md) {
final MDBaseNode parentScopeNode = md.getScope() != MDVoidNode.INSTANCE ? md.getScope() : md.getFile();
parent = buildLocation(parentScopeNode);
kind = LLVMSourceLocation.Kind.LABEL;
file = fileExtractor.extractFile(md);
name = MDNameExtractor.getName(md.getName());
line = md.getLine();
}
}
private LazySourceSectionImpl buildSection(MDFile file, long startLine, long startCol) {
if (file == null) {
return null;
}
final String relPath = MDString.getIfInstance(file.getMDFile());
if (relPath == null || relPath.isEmpty()) {
return null;
}
TruffleFile[] sourceFiles = getSourceFiles(file);
return new LazySourceSectionImpl(sources, sourceFiles, relPath, (int) startLine, (int) startCol);
}
private static Source asSource(Map<String, Source> sources, TruffleFile[] sourceFiles, String path) {
if (sources.containsKey(path)) {
return sources.get(path);
} else if (path == null) {
return null;
}
String mimeType = getMimeType(path);
Source source = null;
if (sourceFiles != null && sourceFiles.length > 0) {
TruffleFile file = sourceFiles[sourceFiles.length - 1];
for (int i = 0; i < sourceFiles.length - 1; i++) {
try {
if (sourceFiles[i].exists()) {
file = sourceFiles[i];
break;
}
} catch (SecurityException e) {
}
}
SourceBuilder builder = Source.newBuilder("llvm", file).mimeType(mimeType);
try {
source = builder.build();
} catch (IOException | SecurityException ex) {
source = builder.content(Source.CONTENT_NONE).build();
}
} else {
final String sourceText = STDIN_FILENAME.equals(path) ? STDIN_SOURCE_TEXT : path;
source = Source.newBuilder("llvm", sourceText, sourceText).mimeType(MIMETYPE_UNAVAILABLE).build();
}
sources.put(path, source);
return source;
}
private final class implements MetadataVisitor {
private MDFile ;
MDFile (MDBaseNode node) {
file = null;
node.accept(FileExtractor.this);
return file;
}
@Override
public void (MDFile md) {
this.file = md;
}
@Override
public void (MDCompileUnit md) {
md.getFile().accept(this);
}
@Override
public void (MDBasicType md) {
md.getFile().accept(this);
}
@Override
public void (MDCompositeType md) {
md.getFile().accept(this);
}
@Override
public void (MDDerivedType md) {
md.getFile().accept(this);
}
@Override
public void (MDGlobalVariable md) {
MDBaseNode fileRef = md.getFile() != MDVoidNode.INSTANCE ? md.getFile() : md.getCompileUnit();
fileRef.accept(this);
}
@Override
public void (MDLexicalBlock md) {
md.getFile().accept(this);
}
@Override
public void (MDLexicalBlockFile md) {
MDBaseNode fileRef = md.getFile() != MDVoidNode.INSTANCE ? md.getFile() : md.getScope();
fileRef.accept(this);
}
@Override
public void (MDLocalVariable md) {
MDBaseNode fileRef = md.getFile() != MDVoidNode.INSTANCE ? md.getFile() : md.getScope();
fileRef.accept(this);
}
@Override
public void (MDMacroFile md) {
md.getFile().accept(this);
}
@Override
public void (MDModule md) {
md.getScope().accept(this);
}
@Override
public void (MDNamespace md) {
md.getFile().accept(this);
}
@Override
public void (MDSubprogram md) {
MDBaseNode fileRef = md.getFile() != MDVoidNode.INSTANCE ? md.getFile() : md.getCompileUnit();
fileRef.accept(this);
}
@Override
public void (MDLocation md) {
md.getScope().accept(this);
}
@Override
public void (MDGlobalVariableExpression md) {
md.getGlobalVariable().accept(this);
}
@Override
public void (MDString md) {
final MDBaseNode typeNode = metadata.identifyType(md.getString());
if (typeNode != null) {
typeNode.accept(this);
}
}
@Override
public void (MDCommonBlock md) {
MDBaseNode fileRef = md.getFile() != MDVoidNode.INSTANCE ? md.getFile() : md.getScope();
fileRef.accept(this);
}
@Override
public void (MDLabel md) {
MDBaseNode fileRef = md.getFile() != MDVoidNode.INSTANCE ? md.getFile() : md.getScope();
fileRef.accept(this);
}
}
}