/*
* Copyright (c) 2016, Oracle and/or its affiliates.
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to
* endorse or promote products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.oracle.truffle.llvm.parser.metadata;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.stream.IntStream;
import java.util.stream.Stream;
public final class MetadataList {
private final MetadataList parent;
private final List<MDBaseNode> metadata = new ArrayList<>();
private final List<MDNamedNode> namedMetadata = new ArrayList<>();
private final List<MDKind> mdKinds = new ArrayList<>();
// TODO associate this with the actual instruction
private final Map<Long, MDAttachment> instructionAttachments = new HashMap<>();
private final List<MDAttachment> functionAttachments = new ArrayList<>();
public MetadataList() {
this(null);
}
private MetadataList(MetadataList parent) {
this.parent = parent;
}
public Map<Long, MDAttachment> getInstructionAttachments() {
return instructionAttachments;
}
public List<MDAttachment> getFunctionAttachments() {
return functionAttachments;
}
public void add(MDBaseNode element) {
metadata.add(element);
}
public void addNamed(MDNamedNode namedNode) {
namedMetadata.add(namedNode);
}
public void addKind(MDKind newKind) {
mdKinds.add(newKind);
}
public void addAttachment(long instructionIndex, MDAttachment attachment) {
instructionAttachments.put(instructionIndex, attachment);
}
public void addAttachment(MDAttachment attachment) {
functionAttachments.add(attachment);
}
public MDBaseNode removeLast() {
if (metadata.size() <= 0) {
throw new IllegalStateException();
}
return metadata.remove(metadata.size() - 1);
}
public MDReference getMDRef(long index) {
return MDReference.fromIndex((int) index, this);
}
public MDReference getMDRefOrNullRef(long index) {
// offsets into the metadatalist are incremented by 1 so 0 can indicate a nullpointer
if (index == 0) {
return MDReference.VOID;
} else {
return getMDRef(index - 1);
}
}
MDBaseNode getFromRef(int index) {
if (parent != null && index < parent.size()) {
return parent.getFromRef(index);
} else {
return metadata.get(index - (parent != null ? parent.size() : 0));
}
}
public MDKind getKind(long id) {
Stream<MDKind> kindStream = mdKinds.stream();
for (MetadataList x = parent; x != null; x = x.parent) {
kindStream = Stream.concat(kindStream, x.mdKinds.stream());
}
return kindStream.filter(kind -> kind.getId() == id).findAny().orElseThrow(() -> new AssertionError("No kind with id: " + id));
}
public int size() {
return parent == null ? metadata.size() : metadata.size() + parent.size();
}
public void print(Consumer<String> target) {
print(target, null);
}
public void print(Consumer<String> target, String functionName) {
if (metadata.isEmpty() && mdKinds.isEmpty()) {
// function-level metadata is often empty
return;
} else if (functionName == null) {
target.accept("Module-Level Metadata");
} else {
target.accept(String.format("Function-Level Metadata: %s", functionName));
}
final int startIndex = (parent != null ? parent.size() : 0);
IntStream.range(0, metadata.size()).mapToObj(i -> String.format("-> !%d %s", i + startIndex, metadata.get(i))).forEachOrdered(target);
namedMetadata.stream().map(n -> String.format("-> !%s", n)).forEach(target);
mdKinds.stream().map(k -> String.format("-> %s", k)).forEach(target);
}
public void accept(MetadataVisitor visitor) {
mdKinds.forEach(md -> md.accept(visitor));
namedMetadata.forEach(md -> md.accept(visitor));
metadata.forEach(md -> md.accept(visitor));
}
@Override
public String toString() {
return String.format("MetadataList [size=%d, parent=%s]", size(), parent);
}
public MetadataList instantiate() {
return new MetadataList(this);
}
}