package sharpen.xobotos.api.interop;
import org.eclipse.jdt.core.dom.ITypeBinding;
import sharpen.core.Sharpen;
import sharpen.core.csharp.ast.*;
import sharpen.core.framework.BindingUtils;
import sharpen.xobotos.api.interop.Signature.Flags;
import sharpen.xobotos.api.interop.Signature.Mode;
import sharpen.xobotos.api.interop.glue.AbstractMember.Visibility;
import sharpen.xobotos.api.interop.glue.*;
import java.util.List;
import java.util.logging.Level;
public class NativeHandleBuilder extends AbstractNativeTypeBuilder {
private final NativeHandle _template;
private final String _functionPrefix;
private final String _destructorName;
private Method _destructor;
public NativeHandleBuilder(NativeBuilder builder, ITypeBinding type, NativeHandle template) {
super(builder, type);
this._template = template;
String fullName = BindingUtils.qualifiedName(type);
String prefix = builder.getConfig().getFunctionPrefix();
if (prefix != null)
_functionPrefix = prefix + "_" + fullName.replace('.', '_');
else
_functionPrefix = fullName.replace('.', '_');
this._destructorName = _functionPrefix + "_destructor";
}
public NativeHandle getTemplate() {
return _template;
}
@Override
public CSTypeReferenceExpression getManagedType() {
return _template.getManagedType();
}
public AbstractTypeReference getNativeClass() {
return new TypeReference(_template.getNativeClass());
}
@Override
public boolean resolve(IMarshalContext context) {
return true;
}
@Override
public boolean build() {
_destructor = createDestructor();
return true;
}
@Override
public boolean createManagedType(CSTypeDeclaration parent) {
final CSTypeDeclaration type = new CSClass(_template.getName(), CSClassModifier.None);
if (_template.getParent() != null) {
type.addBaseType(_template.getParent().getManagedType());
if (_template.getField() != null) {
Sharpen.Log(Level.SEVERE,
"Cannot use both 'parent' and 'field' in <native-handle> for type '%s'",
BindingUtils.qualifiedName(getType()));
return false;
}
} else {
type.addBaseType(new CSTypeReference("System.Runtime.InteropServices.SafeHandle"));
if (_template.getField() == null) {
Sharpen.Log(Level.SEVERE, "Missing 'field' in <native-handle> for type '%s'",
BindingUtils.qualifiedName(getType()));
return false;
}
}
final CSTypeReference thisTypeRef = new CSTypeReference(_template.getName());
final CSTypeReference intPtr = new CSTypeReference("System.IntPtr");
final CSExpression handleRef = new CSReferenceExpression("handle");
final CSExpression thisHandleRef = new CSMemberReferenceExpression(new CSThisExpression(), "handle");
final CSExpression zeroRef = new CSReferenceExpression("System.IntPtr.Zero");
{
CSConstructor ctor = new CSConstructor();
if (_template.getParent() == null) {
CSExpression base = new CSBaseExpression();
CSConstructorInvocationExpression cie = new CSConstructorInvocationExpression(base);
cie.addArgument(zeroRef);
cie.addArgument(new CSBoolLiteralExpression(true));
ctor.chainedConstructorInvocation(cie);
}
type.addMember(ctor);
}
{
CSConstructor ctor = new CSConstructor();
ctor.addParameter("handle", intPtr);
if (_template.getParent() == null) {
CSExpression base = new CSBaseExpression();
CSConstructorInvocationExpression cie = new CSConstructorInvocationExpression(base);
cie.addArgument(zeroRef);
cie.addArgument(new CSBoolLiteralExpression(true));
ctor.chainedConstructorInvocation(cie);
}
ctor.body().addStatement(new CSInfixExpression("=", thisHandleRef, handleRef));
type.addMember(ctor);
}
if (_template.getParent() != null) {
parent.addMember(type);
return true;
}
{
final CSMethod destructor = new CSMethod(_destructorName);
destructor.dllImport(getConfig().getDllImportAttribute());
destructor.modifier(CSMethodModifier.Extern);
destructor.visibility(CSVisibility.Private);
destructor.returnType(new CSTypeReference("void"));
destructor.addParameter(new CSVariableDeclaration("handle", intPtr));
type.addMember(destructor);
}
{
CSProperty prop = new CSProperty("Handle", intPtr);
prop.visibility(CSVisibility.Internal);
CSBlock getter = new CSBlock();
getter.addStatement(new CSReturnStatement(-1, handleRef));
prop.getter(getter);
type.addMember(prop);
}
{
CSField zero = new CSField("Zero", thisTypeRef, CSVisibility.Public);
zero.addModifier(CSFieldModifier.Static);
zero.addModifier(CSFieldModifier.Readonly);
zero.initializer(new CSConstructorInvocationExpression(thisTypeRef));
type.addMember(zero);
}
{
CSMethod release = new CSMethod("ReleaseHandle");
release.returnType(new CSTypeReference("bool"));
release.modifier(CSMethodModifier.Override);
release.visibility(CSVisibility.Protected);
CSExpression dtor = new CSReferenceExpression(_destructorName);
CSMethodInvocationExpression mie = new CSMethodInvocationExpression(dtor);
mie.addArgument(handleRef);
CSExpression check = new CSInfixExpression("!=", handleRef, zeroRef);
CSIfStatement ifstm = new CSIfStatement(-1, check);
ifstm.trueBlock().addStatement(mie);
release.body().addStatement(ifstm);
release.body().addStatement(new CSInfixExpression("=", handleRef, zeroRef));
release.body().addStatement(new CSReturnStatement(-1, new CSBoolLiteralExpression(true)));
type.addMember(release);
}
{
CSProperty isInvalid = new CSProperty("IsInvalid", new CSTypeReference("bool"));
isInvalid.visibility(CSVisibility.Public);
isInvalid.modifier(CSMethodModifier.Override);
CSBlock getter2 = new CSBlock();
CSExpression check = new CSInfixExpression("==", handleRef, zeroRef);
getter2.addStatement(new CSReturnStatement(-1, check));
isInvalid.getter(getter2);
type.addMember(isInvalid);
}
if (_template.getProperty() != null) {
CSProperty accessor = new CSProperty(_template.getProperty(),
new CSTypeReference(_template.getName()));
accessor.visibility(CSVisibility.Internal);
CSBlock getter3 = new CSBlock();
getter3.addStatement(new CSReturnStatement(-1, new CSReferenceExpression(_template.getField())));
accessor.getter(getter3);
parent.addMember(accessor);
}
if (_template.getParent() == null) {
parent.addBaseType(new CSTypeReference("System.IDisposable"));
CSMethod dispose = new CSMethod("Dispose");
dispose.returnType(new CSTypeReference("void"));
dispose.visibility(CSVisibility.Public);
CSExpression fieldRef = new CSReferenceExpression(_template.getField());
CSExpression disposeRef = new CSMemberReferenceExpression(fieldRef, "Dispose");
dispose.body().addStatement(new CSMethodInvocationExpression(disposeRef));
parent.addMember(dispose);
}
parent.addMember(type);
return true;
}
@Override
public boolean createNativeType(CompilationUnit unit) {
unit.addMember(_destructor);
return true;
}
@Override
public boolean createHeader(CompilationUnitHeader header) {
return true;
}
private Method createDestructor() {
Method dtor = new Method(_destructorName, new TypeReference("void"), Visibility.PUBLIC,
Method.Flags.EXPORT);
NativeVariable ptr = NativeVariable.createParameter(dtor, getNativeClass(), "ptr",
NativeVariable.Flags.BYREF);
final Expression unrefExpr = new ReferenceExpression("SkSafeUnref");
if (_template.hasRefCount())
dtor.getBody().addStatement(new MethodInvocation(unrefExpr, ptr.getReference()));
else
dtor.getBody().addStatement(new DestructorInvocation(ptr.getReference()));
return dtor;
}
@Override
public CSTypeReferenceExpression getPInvokeType() {
return _template.getManagedType();
}
@Override
public AbstractTypeReference getNativeType() {
return new TypeReference(_template.getNativeClass());
}
@Override
public AbstractTypeReference getRealNativeType() {
return new TypeReference(_template.getNativeClass());
}
@Override
public AbstractTypeReference getNativePInvokeType() {
return new TypeReference(_template.getNativeClass());
}
@Override
public void marshalManaged(IManagedMarshalContext context, CSExpression expr, Mode mode, Flags flags) {
final CSTypeReferenceExpression pinvokeType;
if (flags == Flags.ELEMENT)
pinvokeType = new CSTypeReference("System.IntPtr");
else
pinvokeType = getManagedType();
final CSExpression arg;
if (flags == Flags.ELEMENT) {
CSExpression infix = new CSInfixExpression("!=", expr, new CSNullLiteralExpression());
CSExpression handleRef = new CSMemberReferenceExpression(expr, "Handle");
CSExpression zeroRef = new CSReferenceExpression("System.IntPtr.Zero");
arg = new CSConditionalExpression(infix, handleRef, zeroRef);
} else if (flags == Flags.ALLOW_NULL) {
CSExpression infix = new CSInfixExpression("!=", expr, new CSNullLiteralExpression());
CSExpression zeroRef = new CSMemberReferenceExpression(getManagedType(), "Zero");
arg = new CSConditionalExpression(infix, expr, zeroRef);
} else {
arg = expr;
}
context.addParameter(null, pinvokeType, arg);
}
@Override
public CSExpression marshalRetval(IManagedReturnContext context, CSExpression expr) {
return expr;
}
@Override
public void marshalNative(INativeMarshalContext context, Mode mode, Flags flags) {
final AbstractTypeReference type = getNativeType();
final NativeVariable param = context.createParameter(null, type);
Expression expr;
if ((mode == Mode.IN) || (mode == Mode.INSTANCE))
expr = new DereferenceExpression(param.getReference());
else
expr = param.getReference();
context.addArgument(expr);
}
@Override
public Expression marshalNativeRetval(Expression expr) {
return expr;
}
@Override
public List<String> getIncludes() {
return _template.getIncludes();
}
}