package com.oracle.svm.hosted.c.query;
import static com.oracle.svm.core.util.VMError.shouldNotReachHere;
import java.util.ArrayList;
import java.util.List;
import java.util.function.IntUnaryOperator;
import org.graalvm.nativeimage.c.struct.RawStructure;
import com.oracle.svm.core.config.ConfigurationValues;
import com.oracle.svm.core.util.UserError;
import com.oracle.svm.hosted.c.CInterfaceError;
import com.oracle.svm.hosted.c.NativeLibraries;
import com.oracle.svm.hosted.c.info.AccessorInfo;
import com.oracle.svm.hosted.c.info.ElementInfo;
import com.oracle.svm.hosted.c.info.NativeCodeInfo;
import com.oracle.svm.hosted.c.info.RawStructureInfo;
import com.oracle.svm.hosted.c.info.SizableInfo.ElementKind;
import com.oracle.svm.hosted.c.info.SizableInfo.SignednessValue;
import com.oracle.svm.hosted.c.info.StructBitfieldInfo;
import com.oracle.svm.hosted.c.info.StructFieldInfo;
import com.oracle.svm.util.ReflectionUtil;
import com.oracle.svm.util.ReflectionUtil.ReflectionUtilError;
import jdk.vm.ci.meta.ResolvedJavaType;
public final class RawStructureLayoutPlanner extends NativeInfoTreeVisitor {
private RawStructureLayoutPlanner(NativeLibraries nativeLibs) {
super(nativeLibs);
}
public static void plan(NativeLibraries nativeLibs, NativeCodeInfo nativeCodeInfo) {
if (!nativeCodeInfo.isBuiltin()) {
return;
}
RawStructureLayoutPlanner planner = new RawStructureLayoutPlanner(nativeLibs);
nativeCodeInfo.accept(planner);
}
@Override
protected void visitRawStructureInfo(RawStructureInfo info) {
if (info.isPlanned()) {
return;
}
ResolvedJavaType type = info.getAnnotatedElement();
for (ResolvedJavaType t : type.getInterfaces()) {
if (!nativeLibs.isPointerBase(t)) {
throw UserError.abort("Type %s must not implement %s", type, t);
}
if (t.equals(nativeLibs.getPointerBaseType())) {
continue;
}
ElementInfo einfo = nativeLibs.findElementInfo(t);
if (!(einfo instanceof RawStructureInfo)) {
throw UserError.abort(new CInterfaceError("Illegal super type " + t + " found", type).getMessage());
}
RawStructureInfo rinfo = (RawStructureInfo) einfo;
rinfo.accept(this);
assert rinfo.isPlanned();
if (info.getParentInfo() != null) {
throw UserError.abort(new CInterfaceError("Only single inheritance of RawStructure types is supported", type).getMessage());
}
info.setParentInfo(rinfo);
}
for (ElementInfo child : new ArrayList<>(info.getChildren())) {
if (child instanceof StructFieldInfo) {
StructFieldInfo fieldInfo = (StructFieldInfo) child;
StructFieldInfo parentFieldInfo = findParentFieldInfo(fieldInfo, info.getParentInfo());
if (parentFieldInfo != null) {
fieldInfo.mergeChildrenAndDelete(parentFieldInfo);
} else {
computeSize(fieldInfo);
}
}
}
planLayout(info);
}
private void computeSize(StructFieldInfo info) {
final int declaredSize;
if (info.isObject()) {
declaredSize = ConfigurationValues.getObjectLayout().getReferenceSize();
} else {
final ResolvedJavaType fieldType;
AccessorInfo accessor = info.getAccessorInfoWithSize();
switch (accessor.getAccessorKind()) {
case GETTER:
fieldType = accessor.getReturnType();
break;
case SETTER:
fieldType = accessor.getValueParameterType();
break;
default:
throw shouldNotReachHere("Unexpected accessor kind " + accessor.getAccessorKind());
}
if (info.getKind() == ElementKind.INTEGER) {
info.getSignednessInfo().setProperty(isSigned(fieldType) ? SignednessValue.SIGNED : SignednessValue.UNSIGNED);
}
declaredSize = getSizeInBytes(fieldType);
}
info.getSizeInfo().setProperty(declaredSize);
}
private void planLayout(RawStructureInfo info) {
int currentOffset = info.getParentInfo() != null ? info.getParentInfo().getSizeInfo().getProperty() : 0;
List<StructFieldInfo> fields = new ArrayList<>();
for (ElementInfo child : info.getChildren()) {
if (child instanceof StructFieldInfo) {
fields.add((StructFieldInfo) child);
} else if (child instanceof StructBitfieldInfo) {
throw UserError.abort("StructBitfield is currently not supported by RawStructures!");
}
}
fields.sort((f1, f2) -> f2.getSizeInfo().getProperty() - f1.getSizeInfo().getProperty());
for (StructFieldInfo finfo : fields) {
assert findParentFieldInfo(finfo, info.getParentInfo()) == null;
int fieldSize = finfo.getSizeInfo().getProperty();
currentOffset = alignOffset(currentOffset, fieldSize);
assert currentOffset % fieldSize == 0;
finfo.getOffsetInfo().setProperty(currentOffset);
currentOffset += fieldSize;
}
int totalSize;
Class<? extends IntUnaryOperator> sizeProviderClass = info.getAnnotatedElement().getAnnotation(RawStructure.class).sizeProvider();
if (sizeProviderClass == IntUnaryOperator.class) {
totalSize = currentOffset;
} else {
IntUnaryOperator sizeProvider;
try {
sizeProvider = ReflectionUtil.newInstance(sizeProviderClass);
} catch (ReflectionUtilError ex) {
throw UserError.abort(ex.getCause(), "The size provider of @%s %s cannot be instantiated via no-argument constructor",
RawStructure.class.getSimpleName(), info.getAnnotatedElement().toJavaName(true));
}
totalSize = sizeProvider.applyAsInt(currentOffset);
if (totalSize < currentOffset) {
throw UserError.abort("The size provider of @%s %s computed size %d which is smaller than the minimum size of %d",
RawStructure.class.getSimpleName(), info.getAnnotatedElement().toJavaName(true), totalSize, currentOffset);
}
}
info.getSizeInfo().setProperty(totalSize);
info.setPlanned();
}
private StructFieldInfo findParentFieldInfo(StructFieldInfo fieldInfo, RawStructureInfo parentInfo) {
if (parentInfo == null) {
return null;
}
StructFieldInfo result;
if (parentInfo.getParentInfo() != null) {
result = findParentFieldInfo(fieldInfo, parentInfo.getParentInfo());
if (result != null) {
return result;
}
}
for (ElementInfo child : parentInfo.getChildren()) {
if (child instanceof StructFieldInfo) {
StructFieldInfo parentFieldInfo = (StructFieldInfo) child;
if (fieldInfo.getName().equals(parentFieldInfo.getName())) {
return parentFieldInfo;
}
}
}
return null;
}
private static int alignOffset(int offset, int fieldSize) {
if (offset % fieldSize == 0) {
return offset;
}
return (offset / fieldSize + 1) * fieldSize;
}
}