package sharpen.xobotos.api.interop;
import static sharpen.core.framework.Environments.my;
import org.eclipse.jdt.core.dom.ITypeBinding;
import sharpen.core.csharp.ast.*;
import sharpen.xobotos.api.bindings.BindingManager;
import sharpen.xobotos.api.interop.NativeMethodBuilder.ElementInfo;
import sharpen.xobotos.api.interop.Signature.Flags;
import sharpen.xobotos.api.interop.Signature.Mode;
import sharpen.xobotos.api.interop.glue.*;
import sharpen.xobotos.api.interop.glue.AbstractMember.Visibility;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
public abstract class HelperClassBuilder extends AbstractNativeTypeBuilder {
private static final boolean GENERATE_ALL = true;
protected static final boolean TRACK_ALLOCATIONS = true;
private final String _name;
private final CSTypeReferenceExpression _managedType;
private final AbstractTypeReference _nativeType;
private final List<Member> _members;
private final List<AbstractMember> _nativeMembers;
private final List<AbstractMember> _nativeHeaders;
private final AbstractTypeReference _structType;
private final StructDefinition _struct;
private final AbstractTypeReference _klassType;
private final ClassDefinition _klass;
private final CSStruct _managedStruct;
private final CSTypeReference _managedStructRef;
private final CSClass _managedHelper;
private final CSTypeReference _managedHelperRef;
private final String _destructorName;
private final boolean _needsHeader;
private final boolean _isShared;
public HelperClassBuilder(ITypeBinding type, String name, NativeBuilder builder,
CSTypeReferenceExpression managedType, AbstractTypeReference nativeType,
boolean needsHeader, boolean isShared) {
super(builder, type);
this._name = name;
this._managedType = managedType;
this._nativeType = nativeType;
this._needsHeader = needsHeader;
this._isShared = isShared;
_members = new ArrayList<Member>();
_nativeMembers = new ArrayList<AbstractMember>();
if (_needsHeader)
_nativeHeaders = new ArrayList<AbstractMember>();
else
_nativeHeaders = null;
_struct = new StructDefinition(_name + "_Struct", Visibility.PRIVATE);
_structType = new TypeReference(_struct.getName());
_nativeMembers.add(_struct);
if (_needsHeader)
_nativeHeaders.add(_struct.getDeclaration());
_klass = new ClassDefinition(_name + "_Helper", Visibility.PUBLIC, _needsHeader);
_klassType = new TypeReference(_klass.getName());
_nativeMembers.add(_klass);
if (_needsHeader)
_nativeHeaders.add(_klass.getDeclaration());
CSStructLayout layout = new CSStructLayout(CSStructLayout.LayoutKind.Sequential);
_managedStruct = new CSStruct(_name + "_Struct", layout);
_managedStruct.visibility(CSVisibility.Private);
_managedStructRef = new CSTypeReference(_managedStruct.name());
_managedHelper = new CSClass(_name + "_Helper", CSClassModifier.Static);
_managedHelper.visibility(CSVisibility.Internal);
_managedHelper.addMember(_managedStruct);
_managedHelper.addAttribute(getMarshalHelperAttribute());
_managedHelperRef = new CSTypeReference(_managedHelper.name());
_destructorName = builder.getFunctionPrefix() + "_" + name + "_destructor";
}
public String getName() {
return _name;
}
public CSTypeDeclaration getManagedHelper() {
return _managedHelper;
}
@Override
public CSTypeReferenceExpression getManagedType() {
return _managedType;
}
@Override
public CSTypeReferenceExpression getPInvokeType() {
return new CSTypeReference("System.IntPtr");
}
public CSTypeReferenceExpression getManagedStructType() {
return _managedStructRef;
}
@Override
public AbstractTypeReference getNativePInvokeType() {
return _structType;
}
@Override
public AbstractTypeReference getNativeType() {
return isByRef() ? new PointerType(_nativeType) : _nativeType;
}
@Override
public AbstractTypeReference getRealNativeType() {
return _nativeType;
}
protected Member addMember(Member member) {
_members.add(member);
return member;
}
@Override
public boolean createManagedType(CSTypeDeclaration parent) {
parent.addMember(_managedHelper);
return true;
}
@Override
public boolean createNativeType(CompilationUnit unit) {
for (AbstractMember member : _nativeMembers)
unit.addMember(member);
return true;
}
@Override
public boolean createHeader(CompilationUnitHeader header) {
if (_needsHeader) {
for (AbstractMember member : _nativeHeaders)
header.addMember(member);
}
return true;
}
protected static boolean isBlittable(ITypeBinding type) {
return my(BindingManager.class).isBlittable(type);
}
protected abstract boolean isBlittable();
protected abstract boolean isByRef();
@Override
public boolean build() {
if (_members.size() > 0)
throw new IllegalStateException();
_members.add(new OwnerMember());
buildMembers();
buildStruct();
buildDefaultCtor();
buildNativeHelpers(UNWRAP, DEEP_UNWRAP, MARSHAL_OUT, WRAP, DEEP_WRAP, WRAP_CONST,
DESTRUCTOR, FREE_MEMBERS, C_DESTRUCTOR);
buildManagedHelpers(NATIVE_SIZE, MANAGED_MARSHAL_IN, MANAGED_MARSHAL_OUT, MANAGED_TO_NATIVE,
NATIVE_TO_MANAGED, FREE_NATIVE_PTR, FREE_MANAGED_PTR, DEEP_FREE_MANAGED_PTR);
if (isBlittable()) {
buildManagedHelpers(PINNED_HANDLE, GET_PINNED_PTR);
}
addMembers();
return true;
}
protected abstract void buildMembers();
@SuppressWarnings("unused")
private void addMembers() {
for (Helper helper : _helpers) {
if (GENERATE_ALL || helper.isUsed())
helper.add();
}
}
private final List<Helper> _helpers = new ArrayList<Helper>();
@SuppressWarnings("unused")
private void buildNativeHelpers(NativeHelper... helpers) {
for (NativeHelper helper : helpers) {
if (GENERATE_ALL || helper.isUsed())
helper.create();
}
}
@SuppressWarnings("unused")
private void buildManagedHelpers(ManagedHelper... helpers) {
for (ManagedHelper helper : helpers) {
if (GENERATE_ALL || helper.isUsed())
helper.create();
}
}
private CSTypeReferenceExpression getNativeHandleInterface() {
return new CSTypeReference("Sharpen.INativeHandle");
}
private CSAttribute getMarshalHelperAttribute() {
CSAttribute attr = new CSAttribute("Sharpen.MarshalHelper");
attr.addArgument(new CSStringLiteralExpression("@\"" + _nativeType.getTypeName() + "\""));
return attr;
}
protected Statement createAssert(Expression expr) {
if (expr == null)
expr = new BoolLiteralExpression(false);
return new ExpressionStatement(new MethodInvocation(new ReferenceExpression("assert"), expr));
}
protected CSStatement createManagedAssert(CSExpression expr) {
CSExpression exType = new CSReferenceExpression("System.InvalidOperationException");
CSStatement stm = new CSThrowStatement(-1, new CSConstructorInvocationExpression(exType));
if (expr == null)
return stm;
CSIfStatement ifStm = new CSIfStatement(-1, expr);
ifStm.trueBlock().addStatement(stm);
return ifStm;
}
private void buildStruct() {
for (Member member : _members) {
if (member.hasValue())
continue;
_struct.addMember(member.createField());
_managedStruct.addMember(member.createManagedField());
}
}
protected CSTypeReferenceExpression getManagedHelperType() {
if (_isShared)
return new CSNestedTypeReference(BindingManager.MANAGED_SHARED_HELPER, _managedHelperRef);
else
return _managedHelperRef;
}
protected static CSExpression getMarshalPtrToStructure(CSExpression ptr, CSTypeReferenceExpression type) {
CSExpression marshal = new CSReferenceExpression("Marshal.PtrToStructure");
CSExpression typeof = new CSTypeofExpression(type);
return new CSCastExpression(type, new CSMethodInvocationExpression(marshal, ptr, typeof));
}
protected abstract class Helper {
private final String _name;
private boolean _used;
protected Helper(String name) {
this._name = name;
}
public String getName() {
return _name;
}
public abstract void create();
public abstract void add();
public boolean isUsed() {
return _used;
}
protected void reference() {
_used = true;
}
}
protected abstract class ManagedHelper extends Helper {
protected ManagedHelper(String name) {
super(name);
}
public CSExpression expr() {
if (_members.size() > 0)
create();
else
reference();
return new CSMemberReferenceExpression(getManagedHelperType(), getName());
}
private CSMember _member;
@Override
public void create() {
if (_member == null) {
reference();
_member = build();
_helpers.add(this);
}
}
@Override
public void add() {
_managedHelper.addMember(_member);
}
protected abstract CSMember build();
}
protected abstract class NativeHelper extends Helper {
protected NativeHelper(String name) {
super(name);
}
public Expression expr() {
if (_members.size() > 0)
create();
else
reference();
return new StaticMemberAccess(_klassType, getName());
}
private Method _method;
@Override
public void create() {
if (_method == null) {
reference();
_method = build();
_helpers.add(this);
}
}
@Override
public void add() {
_klass.addMember(_method);
}
protected abstract Method build();
}
protected final NativeHelper UNWRAP = new NativeHelper("unwrap") {
@Override
protected Method build() {
AbstractTypeReference retType = new PointerType(_structType);
Method unwrap = new Method(getName(), retType, Visibility.PUBLIC, Method.Flags.STATIC);
NativeVariable src = NativeVariable.createParameter(unwrap, _nativeType, "src",
NativeVariable.Flags.CLASS, NativeVariable.Flags.BYREF);
Expression nullPtr = new NullLiteralExpression();
Expression nullCheck = new BinaryOperator("==", src.getReference(), nullPtr);
IfStatement ifStm = new IfStatement(nullCheck);
ifStm.getThenBlock().addStatement(new ReturnStatement(nullPtr));
unwrap.getBody().addStatement(ifStm);
Expression ctorCall = new ConstructorInvocation(_structType);
NativeVariable retval = NativeVariable.createLocal(unwrap.getBody(), _structType, "retval",
ctorCall, NativeVariable.Flags.BYREF);
Expression retvalRef = retval.getReference();
unwrap.getBody().addStatement(
new MethodInvocation(DEEP_UNWRAP.expr(), src.getDereferenceExpr(), retvalRef));
unwrap.getBody().addStatement(new ReturnStatement(retvalRef));
return unwrap;
}
};
protected final NativeHelper DEEP_UNWRAP = new NativeHelper("unwrap") {
@Override
protected Method build() {
Method unwrap = new Method(getName(), new TypeReference("void"), Visibility.PUBLIC,
Method.Flags.STATIC);
NativeVariable from = NativeVariable.createParameter(unwrap, _nativeType, "from",
NativeVariable.Flags.CLASS, NativeVariable.Flags.REF);
NativeVariable to = NativeVariable.createParameter(unwrap, _structType, "to",
NativeVariable.Flags.BYREF);
for (Member member : _members) {
Statement stm = member.unwrap(from, to);
if (stm != null)
unwrap.getBody().addStatement(stm);
}
return unwrap;
}
};
protected final NativeHelper WRAP = new NativeHelper("wrap") {
@Override
protected Method build() {
Method wrap = new Method(getName(), new PointerType(_nativeType), Visibility.PUBLIC,
Method.Flags.STATIC);
NativeVariable src = NativeVariable.createParameter(wrap, _structType, "src",
NativeVariable.Flags.CONST, NativeVariable.Flags.BYREF);
Expression nullPtr = new NullLiteralExpression();
Expression nullCheck = new BinaryOperator("==", src.getReference(), nullPtr);
IfStatement ifStm = new IfStatement(nullCheck);
ifStm.getThenBlock().addStatement(new ReturnStatement(nullPtr));
wrap.getBody().addStatement(ifStm);
buildWrap(wrap, src);
return wrap;
}
};
protected void buildWrap(Method method, NativeVariable src) {
ConstructorInvocation ctorCall = new ConstructorInvocation(_nativeType);
for (Member member : _members) {
if (member.passToConstructor())
ctorCall.addArgument(member.getReference(src));
}
NativeVariable retval = NativeVariable.createLocal(method.getBody(), _nativeType, "retval", ctorCall,
NativeVariable.Flags.BYREF);
Expression srcDeref = src.getDereferenceExpr();
method.getBody().addStatement(new MethodInvocation(DEEP_WRAP.expr(), srcDeref, retval.getReference()));
method.getBody().addStatement(new ReturnStatement(retval.getReference()));
}
protected final NativeHelper DEEP_WRAP = new NativeHelper("wrap") {
@Override
protected Method build() {
Method method = new Method(getName(), new TypeReference("void"), Visibility.PUBLIC,
Method.Flags.STATIC);
NativeVariable from = NativeVariable.createParameter(method, _structType, "from",
NativeVariable.Flags.CONST);
NativeVariable to = NativeVariable.createParameter(method, _nativeType, "to",
NativeVariable.Flags.CLASS, NativeVariable.Flags.BYREF);
for (Member member : _members) {
Statement stm = member.wrap(from, to);
if (stm != null)
method.getBody().addStatement(stm);
}
return method;
}
};
protected final NativeHelper WRAP_CONST = new NativeHelper("wrapConst") {
@Override
protected Method build() {
AbstractTypeReference retType = new ConstPointerType(_nativeType);
Method wrapConst = new Method(getName(), retType, Visibility.PUBLIC, Method.Flags.STATIC);
Parameter src = new Parameter(new ConstPointerType(_structType), "src");
Expression srcRef = new VariableReference(src);
wrapConst.addParameter(src);
MethodInvocation call = new MethodInvocation(WRAP.expr(), srcRef);
Expression templateRef = new TemplateFunctionReference("const_cast", retType);
MethodInvocation cast = new MethodInvocation(templateRef, call);
wrapConst.getBody().addStatement(new ReturnStatement(cast));
return wrapConst;
}
};
protected final NativeHelper MARSHAL_OUT = new NativeHelper("marshalOut") {
@Override
protected Method build() {
Method marshal = new Method(getName(), new TypeReference("void"), Visibility.PUBLIC,
Method.Flags.STATIC);
NativeVariable from = NativeVariable.createParameter(marshal, _nativeType, "from",
NativeVariable.Flags.CLASS, NativeVariable.Flags.CONST);
NativeVariable to = NativeVariable.createParameter(marshal, _structType, "to",
NativeVariable.Flags.BYREF);
for (Member member : _members) {
Statement stm = member.marshalOut(from, to);
if (stm != null)
marshal.getBody().addStatement(stm);
}
return marshal;
}
};
private void buildDefaultCtor() {
Constructor ctor = new Constructor(_klassType, Visibility.PRIVATE);
_klass.addMember(ctor.getDeclaration());
}
protected final NativeHelper DESTRUCTOR = new NativeHelper("destructor") {
@Override
protected Method build() {
Method dtor = new Method(getName(), new TypeReference("void"), Visibility.PUBLIC,
Method.Flags.STATIC);
NativeVariable obj = NativeVariable.createParameter(dtor, _structType, "obj",
NativeVariable.Flags.BYREF);
IfStatement ifStm = new IfStatement(new BinaryOperator("==", obj.getReference(),
new NullLiteralExpression()));
ifStm.getThenBlock().addStatement(new ReturnStatement());
dtor.getBody().addStatement(ifStm);
dtor.getBody()
.addStatement(new MethodInvocation(FREE_MEMBERS.expr(), obj
.getDereferenceExpr()));
dtor.getBody().addStatement(new DestructorInvocation(obj.getReference()));
return dtor;
}
};
protected final NativeHelper FREE_MEMBERS = new NativeHelper("freeMembers") {
@Override
protected Method build() {
Method method = new Method(getName(), new TypeReference("void"), Visibility.PUBLIC,
Method.Flags.STATIC);
NativeVariable obj = NativeVariable.createParameter(method, _structType, "obj",
NativeVariable.Flags.REF);
for (Member member : _members) {
Statement stm = member.freeMembers(obj);
if (stm != null)
method.getBody().addStatement(stm);
}
return method;
}
};
protected final NativeHelper C_DESTRUCTOR = new NativeHelper("destructor") {
private Method _dtor;
@Override
protected Method build() {
_dtor = new Method(_destructorName, new TypeReference("void"), Visibility.PUBLIC,
Method.Flags.EXPORT);
Parameter obj = new Parameter(new PointerType(_structType), "obj");
Expression objRef = new VariableReference(obj);
_dtor.addParameter(obj);
DESTRUCTOR.reference();
_dtor.getBody().addStatement(new MethodInvocation(DESTRUCTOR.expr(), objRef));
return _dtor;
}
@Override
public void add() {
_nativeMembers.add(_dtor);
}
};
protected final ManagedHelper NATIVE_SIZE = new ManagedHelper("NativeSize") {
@Override
protected CSMember build() {
CSProperty prop = new CSProperty(getName(), new CSTypeReference("int"));
prop.visibility(CSVisibility.Internal);
prop.modifier(CSMethodModifier.Static);
CSBlock block = new CSBlock();
block.addStatement(new CSReturnStatement(-1, new CSMethodInvocationExpression(
new CSReferenceExpression("Marshal.SizeOf"), new CSTypeofExpression(_managedStructRef))));
prop.getter(block);
return prop;
}
};
protected final ManagedHelper MANAGED_MARSHAL_IN = new ManagedHelper("MarshalIn") {
@Override
protected CSMember build() {
CSMethod method = new CSMethod(getName());
method.returnType(new CSTypeReference("void"));
method.visibility(CSVisibility.Public);
method.modifier(CSMethodModifier.Static);
final ManagedVariable ptr = new ManagedVariable("ptr", new CSTypeReference("System.IntPtr"));
method.addParameter(ptr.getDeclaration());
final ManagedVariable arg = new ManagedVariable("arg", _managedType);
method.addParameter(arg.getDeclaration());
final ManagedVariable obj = new ManagedVariable("obj", _managedStructRef);
obj.getDeclaration().initializer(new CSConstructorInvocationExpression(_managedStructRef));
method.body().addStatement(new CSDeclarationStatement(-1, obj.getDeclaration()));
for (Member member : _members) {
CSStatement stm = member.marshalIn(arg, obj, ptr);
if (stm != null)
method.body().addStatement(stm);
}
method.body().addStatement(
new CSMethodInvocationExpression(
new CSReferenceExpression("Marshal.StructureToPtr"),
obj.getReference(), ptr.getReference(),
new CSBoolLiteralExpression(false)));
return method;
}
};
protected final ManagedHelper MANAGED_MARSHAL_OUT = new ManagedHelper("MarshalOut") {
@Override
protected CSMember build() {
CSMethod method = new CSMethod(getName());
method.returnType(new CSTypeReference("void"));
method.visibility(CSVisibility.Public);
method.modifier(CSMethodModifier.Static);
final ManagedVariable ptr = new ManagedVariable("ptr", new CSTypeReference("System.IntPtr"));
method.addParameter(ptr.getDeclaration());
final ManagedVariable arg = new ManagedVariable("arg", _managedType);
method.addParameter(arg.getDeclaration());
final ManagedVariable obj = new ManagedVariable("obj", _managedStructRef);
method.body().addStatement(new CSDeclarationStatement(-1, obj.getDeclaration()));
obj.getDeclaration().initializer(
getMarshalPtrToStructure(ptr.getReference(), _managedStructRef));
for (Member member : _members) {
CSStatement stm = member.marshalOut(arg, obj, ptr);
if (stm != null)
method.body().addStatement(stm);
}
return method;
}
};
protected final ManagedHelper MANAGED_TO_NATIVE = new ManagedHelper("ManagedToNative") {
@Override
protected CSMember build() {
CSMethod method = new CSMethod(getName());
method.returnType(new CSTypeReference("System.IntPtr"));
method.visibility(CSVisibility.Public);
method.modifier(CSMethodModifier.Static);
final ManagedVariable arg = new ManagedVariable("arg", _managedType);
method.addParameter(arg.getDeclaration());
final CSExpression nullPtr = new CSNullLiteralExpression();
final CSExpression zeroPtr = new CSReferenceExpression("System.IntPtr.Zero");
final CSExpression argRef = arg.getReference();
CSIfStatement nullCheck = new CSIfStatement(-1, new CSInfixExpression("==", argRef, nullPtr));
nullCheck.trueBlock().addStatement(new CSReturnStatement(-1, zeroPtr));
method.body().addStatement(nullCheck);
final ManagedVariable ptr = new ManagedVariable("ptr", new CSTypeReference("System.IntPtr"));
method.body().addStatement(new CSDeclarationStatement(-1, ptr.getDeclaration()));
ptr.getDeclaration().initializer(new CSMethodInvocationExpression(
new CSReferenceExpression("Marshal.AllocHGlobal"), computeNativeSize(argRef)));
method.body().addStatement(
new CSMethodInvocationExpression(MANAGED_MARSHAL_IN.expr(),
ptr.getReference(), arg.getReference()));
method.body().addStatement(new CSReturnStatement(-1, ptr.getReference()));
return method;
}
};
protected final ManagedHelper NATIVE_TO_MANAGED = new ManagedHelper("NativeToManaged") {
@Override
protected CSMethod build() {
CSMethod method = new CSMethod(getName());
method.returnType(_managedType);
method.visibility(CSVisibility.Public);
method.modifier(CSMethodModifier.Static);
final ManagedVariable ptr = new ManagedVariable("ptr", new CSTypeReference("System.IntPtr"));
method.addParameter(ptr.getDeclaration());
final CSExpression nullPtr = new CSNullLiteralExpression();
final CSExpression zeroPtr = new CSReferenceExpression("System.IntPtr.Zero");
final CSExpression argRef = ptr.getReference();
CSIfStatement nullCheck = new CSIfStatement(-1, new CSInfixExpression("==", argRef, zeroPtr));
nullCheck.trueBlock().addStatement(new CSReturnStatement(-1, nullPtr));
method.body().addStatement(nullCheck);
final ManagedVariable obj = new ManagedVariable("obj", _managedStructRef);
method.body().addStatement(new CSDeclarationStatement(-1, obj.getDeclaration()));
obj.getDeclaration().initializer(
getMarshalPtrToStructure(ptr.getReference(), _managedStructRef));
final ManagedVariable arg = new ManagedVariable("arg", _managedType);
arg.getDeclaration().initializer(createInstance(obj));
method.body().addStatement(new CSDeclarationStatement(-1, arg.getDeclaration()));
for (Member member : _members) {
CSStatement stm = member.nativeToManaged(arg, obj, ptr);
if (stm != null)
method.body().addStatement(stm);
}
method.body().addStatement(new CSReturnStatement(-1, arg.getReference()));
return method;
}
};
protected final ManagedHelper FREE_MANAGED_PTR = new ManagedHelper("FreeManagedPtr") {
@Override
protected CSMember build() {
CSMethod method = new CSMethod(getName());
method.returnType(new CSTypeReference("void"));
method.visibility(CSVisibility.Public);
method.modifier(CSMethodModifier.Static);
final ManagedVariable ptr = new ManagedVariable("ptr", new CSTypeReference("System.IntPtr"));
method.addParameter(ptr.getDeclaration());
CSIfStatement nullCheck = new CSIfStatement(-1, new CSInfixExpression("==", ptr.getReference(),
new CSReferenceExpression("System.IntPtr.Zero")));
nullCheck.trueBlock().addStatement(new CSReturnStatement(-1, null));
method.body().addStatement(nullCheck);
method.body().addStatement(
new CSMethodInvocationExpression(DEEP_FREE_MANAGED_PTR.expr(), ptr
.getReference()));
method.body().addStatement(
new CSMethodInvocationExpression(new CSReferenceExpression(
"Marshal.FreeHGlobal"),
ptr.getReference()));
return method;
}
};
protected final ManagedHelper DEEP_FREE_MANAGED_PTR = new ManagedHelper("FreeManagedPtr_inner") {
@Override
protected CSMember build() {
CSMethod method = new CSMethod(getName());
method.returnType(new CSTypeReference("void"));
method.visibility(CSVisibility.Public);
method.modifier(CSMethodModifier.Static);
final ManagedVariable ptr = new ManagedVariable("ptr", new CSTypeReference("System.IntPtr"));
method.addParameter(ptr.getDeclaration());
final ManagedVariable obj = new ManagedVariable("obj", _managedStructRef);
obj.getDeclaration().initializer(
getMarshalPtrToStructure(ptr.getReference(), _managedStructRef));
method.body().addStatement(new CSDeclarationStatement(-1, obj.getDeclaration()));
for (Member member : _members) {
CSStatement stm = member.free(obj);
if (stm != null)
method.body().addStatement(stm);
}
return method;
}
};
protected final ManagedHelper FREE_NATIVE_PTR = new ManagedHelper("FreeNativePtr") {
private CSMethod _pinvoke;
@Override
protected CSMember build() {
final ManagedVariable ptr = new ManagedVariable("ptr", new CSTypeReference("System.IntPtr"));
_pinvoke = new CSMethod(_destructorName);
_pinvoke.dllImport(getConfig().getDllImportAttribute());
_pinvoke.modifier(CSMethodModifier.Extern);
_pinvoke.visibility(CSVisibility.Private);
_pinvoke.returnType(new CSTypeReference("void"));
_pinvoke.addParameter(ptr.getDeclaration());
CSMethod method = new CSMethod(getName());
method.returnType(new CSTypeReference("void"));
method.visibility(CSVisibility.Public);
method.modifier(CSMethodModifier.Static);
method.addParameter(ptr.getDeclaration());
CSExpression dtorRef = new CSReferenceExpression(_pinvoke.name());
method.body().addStatement(new CSMethodInvocationExpression(dtorRef, ptr.getReference()));
return method;
}
@Override
public void reference() {
super.reference();
C_DESTRUCTOR.reference();
}
@Override
public void add() {
super.add();
_managedHelper.addMember(_pinvoke);
}
};
protected final ManagedHelper GET_PINNED_PTR = new ManagedHelper("GetPinnedPtr") {
@Override
protected CSMethod build() {
CSMethod method = new CSMethod(getName());
method.returnType(getNativeHandleInterface());
method.visibility(CSVisibility.Public);
method.modifier(CSMethodModifier.Static);
final ManagedVariable arg = new ManagedVariable("arg", _managedType);
method.addParameter(arg.getDeclaration());
final CSExpression nullPtr = new CSNullLiteralExpression();
final CSExpression argRef = arg.getReference();
CSIfStatement nullCheck = new CSIfStatement(-1, new CSInfixExpression("==", argRef, nullPtr));
nullCheck.trueBlock().addStatement(new CSReturnStatement(-1, nullPtr));
method.body().addStatement(nullCheck);
PINNED_HANDLE.expr();
final ManagedVariable pinned = new ManagedVariable("pinned", new CSTypeReference("PinnedHandle"));
pinned.getDeclaration().initializer(
new CSConstructorInvocationExpression(new CSTypeReference("PinnedHandle")));
method.body().addStatement(pinned.getDeclarationStatement());
final ManagedVariable obj = new ManagedVariable("obj", _managedStructRef);
obj.getDeclaration().initializer(new CSConstructorInvocationExpression(_managedStructRef));
method.body().addStatement(obj.getDeclarationStatement());
for (Member member : _members) {
CSStatement stm = member.getPinnedPtr(arg, obj, pinned);
if (stm != null)
method.body().addStatement(stm);
}
CSExpression handleRef = new CSMemberReferenceExpression(pinned.getReference(), "handle");
CSExpression ptrRef = new CSMemberReferenceExpression(pinned.getReference(), "ptr");
CSExpression createHandle = new CSMethodInvocationExpression(
new CSReferenceExpression("GCHandle.Alloc"), obj.getReference(),
new CSReferenceExpression("GCHandleType.Pinned"));
method.body().addStatement(new CSInfixExpression("=", handleRef, createHandle));
CSExpression getAddr = new CSMethodInvocationExpression(new CSMemberReferenceExpression(
handleRef, "AddrOfPinnedObject"));
method.body().addStatement(new CSInfixExpression("=", ptrRef, getAddr));
method.body().addStatement(new CSReturnStatement(-1, pinned.getReference()));
return method;
}
};
protected final ManagedHelper PINNED_HANDLE = new ManagedHelper("PinnedHandle") {
@Override
protected CSMember build() {
CSStruct struct = new CSStruct(getName());
struct.addBaseType(getNativeHandleInterface());
CSField handle = new CSField("handle", new CSTypeReference("GCHandle"), CSVisibility.Public);
struct.addMember(handle);
CSField ptr = new CSField("ptr", new CSTypeReference("System.IntPtr"), CSVisibility.Public);
struct.addMember(ptr);
CSProperty prop = new CSProperty("Address", new CSTypeReference("System.IntPtr"));
prop.visibility(CSVisibility.Public);
struct.addMember(prop);
prop.getter(new CSBlock());
prop.getter().addStatement(new CSReturnStatement(-1, new CSReferenceExpression(ptr.name())));
CSMethod free = new CSMethod("Free");
free.returnType(new CSTypeReference("void"));
free.visibility(CSVisibility.Public);
struct.addMember(free);
CSExpression freeRef = new CSMemberReferenceExpression(new CSReferenceExpression(handle.name()), "Free");
free.body().addStatement(new CSMethodInvocationExpression(freeRef));
for (Member member : _members) {
member.createPinnedPtr(struct, free);
}
return struct;
}
};
protected abstract CSExpression computeNativeSize(CSExpression expr);
protected abstract CSExpression createInstance(ManagedVariable obj);
@Override
public void marshalNative(INativeMarshalContext context, Mode mode, Flags flags) {
final NativeVariable param = NativeVariable.createParameter(context, _structType, "ptr",
mode == Mode.OUT ? NativeVariable.Flags.OUT : NativeVariable.Flags.BYREF);
EnumSet<NativeVariable.Flags> e = EnumSet.of(NativeVariable.Flags.CLASS);
final Expression wrap;
if (mode == Mode.OUT) {
wrap = null;
} else if (mode == Mode.REF) {
wrap = new MethodInvocation(WRAP.expr(), param.getReference());
e.add(NativeVariable.Flags.BYREF);
} else {
wrap = new MethodInvocation(WRAP_CONST.expr(), param.getReference());
e.add(NativeVariable.Flags.BYREF);
e.add(NativeVariable.Flags.CONST);
}
final NativeVariable var = NativeVariable.createLocal(context, _nativeType, null, wrap, e);
if (mode == Mode.OUT)
context.addArgument(var.getAddressOfExpr());
else if (flags == Flags.ALLOW_NULL)
context.addArgument(var.getReference());
else
context.addArgument(var.getDereferenceExpr());
if (!isBlittable() && (mode == Mode.REF)) {
Expression nullPtr = new NullLiteralExpression();
Expression nullCheck = new BinaryOperator("!=", param.getReference(), nullPtr);
IfStatement ifStm = new IfStatement(nullCheck);
ifStm.getThenBlock().addStatement(
new MethodInvocation(MARSHAL_OUT.expr(),
var.getDereferenceExpr(), param.getReference()));
context.addPostStatement(ifStm);
} else if (mode == Mode.OUT) {
context.addPostStatement(new AssignmentStatement(param.getDereferenceExpr(),
new MethodInvocation(UNWRAP.expr(), var.getAddressOfExpr())));
}
if (mode != Mode.OUT)
context.addPostStatement(new DestructorInvocation(var.getReference()));
}
@Override
public void marshalManaged(IManagedMarshalContext context, CSExpression expr, Mode mode, Flags flags) {
if (isBlittable() && (mode != Mode.OUT)) {
marshalBlittable(context, expr, mode, flags);
return;
}
final CSTypeReference intPtr = new CSTypeReference("System.IntPtr");
ManagedVariable arg = new ManagedVariable(context.getVariableName("ptr"), intPtr, null,
mode == Mode.OUT ? ManagedVariable.Flags.OUT : null);
ManagedVariable ptr = new ManagedVariable(context.getVariableName("ptr"), intPtr,
new CSReferenceExpression("System.IntPtr.Zero"));
context.addParameter(null, arg.getDeclaration().type(),
mode == Mode.OUT ? ptr.getOutReference() : ptr.getReference());
CSExpression cleanupFunc = mode == Mode.OUT ? FREE_NATIVE_PTR.expr() : FREE_MANAGED_PTR.expr();
CSExpression cleanup = new CSMethodInvocationExpression(cleanupFunc, ptr.getReference());
context.addDeclaration(ptr.getDeclaration(), new CSExpressionStatement(-1, cleanup));
if (mode != Mode.OUT) {
CSExpression marshal = new CSInfixExpression("=", ptr.getReference(),
new CSMethodInvocationExpression(MANAGED_TO_NATIVE.expr(), expr));
context.addPreStatement(new CSExpressionStatement(-1, marshal));
}
if ((mode == Mode.REF) || (mode == Mode.OUT)) {
CSExpression marshal = new CSMethodInvocationExpression(MANAGED_MARSHAL_OUT.expr(),
ptr.getReference(), expr);
context.addPostStatement(new CSExpressionStatement(-1, marshal));
}
}
@Override
public CSExpression marshalRetval(IManagedReturnContext context, CSExpression expr) {
ManagedVariable ptr = context.createVariable("ptr", new CSTypeReference("System.IntPtr"), expr);
context.createRetval(new CSMethodInvocationExpression(NATIVE_TO_MANAGED.expr(), ptr.getReference()));
CSExpression freeInvoc = new CSMethodInvocationExpression(FREE_NATIVE_PTR.expr(), ptr.getReference());
context.addStatement(new CSExpressionStatement(-1, freeInvoc));
return null;
}
private void marshalBlittable(IManagedMarshalContext context, CSExpression expr, Mode mode, Flags flags) {
ManagedVariable handle = new ManagedVariable(context.getVariableName("handle"),
getNativeHandleInterface(), new CSNullLiteralExpression());
CSExpression nullCheck = new CSInfixExpression("!=", handle.getReference(),
new CSNullLiteralExpression());
CSIfStatement cleanup = new CSIfStatement(-1, nullCheck);
cleanup.trueBlock().addStatement(new CSMethodInvocationExpression(new CSMemberReferenceExpression(
handle.getReference(), "Free")));
context.addDeclaration(handle.getDeclaration(), cleanup);
CSExpression marshal = new CSInfixExpression("=", handle.getReference(),
new CSMethodInvocationExpression(GET_PINNED_PTR.expr(), expr));
context.addPreStatement(new CSExpressionStatement(-1, marshal));
CSExpression addrRef = new CSMemberReferenceExpression(handle.getReference(), "Address");
CSExpression arg;
if (flags == Flags.ALLOW_NULL)
arg = new CSConditionalExpression(nullCheck, addrRef, new CSReferenceExpression(
"System.IntPtr.Zero"));
else
arg = addrRef;
context.addParameter(null, new CSTypeReference("System.IntPtr"), arg);
}
@Override
public Expression marshalNativeRetval(Expression expr) {
return new MethodInvocation(UNWRAP.expr(), expr);
}
protected abstract static class Member {
private final AbstractTypeReference _type;
private final CSTypeReferenceExpression _managed;
private final String _name;
private final String _managedName;
private final EnumSet<Flags> _flags;
public Member(AbstractTypeReference type, CSTypeReferenceExpression managed, String name,
String managedName, Flags... flags) {
this(type, managed, name, managedName, buildSet(Flags.class, flags));
}
public Member(AbstractTypeReference type, CSTypeReferenceExpression managed, String name,
String managedName, EnumSet<Flags> flags) {
this._flags = flags;
this._type = type;
this._managed = managed;
this._name = name;
this._managedName = managedName;
}
public enum Flags {
FUNCTION,
PASS_TO_CTOR,
BYREF,
POINTER,
HAS_VALUE
}
public String getName() {
return _name;
}
public String getManagedName() {
return _managedName;
}
protected final AbstractTypeReference getNativeType() {
if (hasValue())
throw new IllegalStateException();
if (isByRef())
return new PointerType(_type);
else
return _type;
}
protected final boolean isFunction() {
return _flags.contains(Flags.FUNCTION);
}
protected final boolean passToConstructor() {
return _flags.contains(Flags.PASS_TO_CTOR);
}
protected final boolean isByRef() {
return _flags.contains(Flags.BYREF);
}
protected final boolean isPointer() {
return _flags.contains(Flags.POINTER);
}
protected final boolean hasValue() {
return _flags.contains(Flags.HAS_VALUE);
}
protected final EnumSet<Flags> getFlags() {
return _flags;
}
public Expression getReference(NativeVariable var) {
Expression varRef = var.getMemberAccess(_name);
if (var.isTarget() && isFunction())
varRef = new MethodInvocation(varRef);
return varRef;
}
public Expression getIndex(NativeVariable var, Expression expr) {
Expression varRef = var.getMemberAccess("item");
return new MethodInvocation(varRef, expr);
}
public CSExpression getReference(ManagedVariable var) {
return new CSMemberReferenceExpression(var.getReference(), _managedName);
}
protected Field createField() {
if (hasValue())
throw new IllegalStateException();
AbstractTypeReference fieldType = _type;
if (isByRef())
fieldType = new PointerType(fieldType);
if (isPointer())
fieldType = new PointerType(fieldType);
return new Field(fieldType, _name, Visibility.PRIVATE);
}
protected final CSField createManagedField() {
if (hasValue())
throw new IllegalStateException();
return new CSField(_managedName, _managed, CSVisibility.Public);
}
protected abstract void createPinnedPtr(CSStruct struct, CSMethod dtor);
protected abstract CSStatement getPinnedPtr(ManagedVariable arg, ManagedVariable obj,
ManagedVariable pinned);
protected abstract CSStatement marshalIn(ManagedVariable arg, ManagedVariable obj, ManagedVariable ptr);
protected abstract CSStatement marshalOut(ManagedVariable arg, ManagedVariable obj, ManagedVariable ptr);
protected abstract CSStatement nativeToManaged(ManagedVariable arg, ManagedVariable obj,
ManagedVariable ptr);
public abstract CSStatement free(ManagedVariable obj);
protected abstract Statement wrap(NativeVariable src, NativeVariable dest);
protected abstract Statement unwrap(NativeVariable src, NativeVariable dest);
protected abstract Statement freeMembers(NativeVariable obj);
protected abstract Statement marshalOut(NativeVariable src, NativeVariable dest);
}
protected abstract static class ElementMember extends Member {
private final ElementInfo _elementInfo;
private final HelperClassBuilder _elementHelper;
protected ElementMember(ElementInfo info, String name, String managedName, Flags... flags) {
this(info, name, managedName, buildSet(Flags.class, flags));
}
protected ElementMember(ElementInfo info, String name, String managedName, EnumSet<Flags> flags) {
super(computeNativeType(info), computeManagedType(info, flags), name, managedName, flags);
this._elementInfo = info;
if (info.isClass()) {
AbstractNativeTypeBuilder builder = info.getTypeBuilder();
if (builder instanceof HelperClassBuilder) {
_elementHelper = (HelperClassBuilder) builder;
if (_elementHelper.isByRef())
getFlags().add(Flags.BYREF);
} else {
_elementHelper = null;
}
} else
_elementHelper = null;
}
private static AbstractTypeReference computeNativeType(ElementInfo info) {
if (info.isClass())
return info.getTypeBuilder().getNativePInvokeType();
else
return info.getNativeType();
}
private static CSTypeReferenceExpression computeManagedType(ElementInfo info, EnumSet<Flags> flags) {
if (flags.contains(Flags.POINTER))
return new CSTypeReference("System.IntPtr");
else
return info.getPInvokeType();
}
protected boolean isClass() {
return _elementHelper != null;
}
protected HelperClassBuilder getHelper() {
return _elementHelper;
}
protected ElementInfo getElementInfo() {
return _elementInfo;
}
protected CSExpression getNativeElementSizeExpr() {
if (isClass())
return getHelper().NATIVE_SIZE.expr();
CSExpression sizeof = new CSReferenceExpression("Marshal.SizeOf");
return new CSMethodInvocationExpression(sizeof, new CSTypeofExpression(
_elementInfo.getManagedType()));
}
protected CSStatement marshalIn(CSExpression addr, CSExpression expr) {
if (isClass()) {
return new CSExpressionStatement(-1, new CSMethodInvocationExpression(
getHelper().MANAGED_MARSHAL_IN.expr(), addr, expr));
} else {
return new CSExpressionStatement(-1, new CSMethodInvocationExpression(
new CSReferenceExpression("Marshal.StructureToPtr"), expr, addr,
new CSBoolLiteralExpression(false)));
}
}
protected CSStatement marshalOut(CSExpression addr, CSExpression expr, CSTypeReferenceExpression type) {
if (isClass()) {
return new CSExpressionStatement(-1, new CSMethodInvocationExpression(
getHelper().MANAGED_MARSHAL_OUT.expr(), addr, expr));
} else {
return new CSExpressionStatement(-1, new CSInfixExpression("=", expr,
getMarshalPtrToStructure(addr, type)));
}
}
protected CSStatement nativeToManaged(CSExpression addr, CSExpression expr,
CSTypeReferenceExpression type) {
CSExpression marshal;
if (isClass())
marshal = new CSMethodInvocationExpression(getHelper().NATIVE_TO_MANAGED.expr(), addr);
else
marshal = getMarshalPtrToStructure(addr, type);
return new CSExpressionStatement(-1, new CSInfixExpression("=", expr, marshal));
}
}
static <T extends Enum<T>> EnumSet<T> buildSet(Class<T> klass, T... args) {
EnumSet<T> set = EnumSet.noneOf(klass);
if (args != null) {
for (T arg : args)
set.add(arg);
}
return set;
}
// Randomly generated numbers; no special meaning.
private static final CSExpression MANAGED_MAGIC = new CSNumberLiteralExpression("0x972f3813");
private static final CSExpression PINNED_MAGIC = new CSNumberLiteralExpression("0x337b4904");
private static final Expression NATIVE_MAGIC = new NumberLiteralExpression("0x7380548b");
private class OwnerMember extends Member {
public OwnerMember() {
super(new TypeReference("uint32_t"), new CSTypeReference("uint"), "_owner", "_owner");
}
@Override
protected void createPinnedPtr(CSStruct struct, CSMethod free) {
;
}
@Override
protected CSStatement getPinnedPtr(ManagedVariable arg, ManagedVariable obj, ManagedVariable pinned) {
return new CSExpressionStatement(-1, new CSInfixExpression("=", getReference(obj),
PINNED_MAGIC));
}
@Override
protected CSStatement marshalIn(ManagedVariable arg, ManagedVariable obj, ManagedVariable ptr) {
return new CSExpressionStatement(-1, new CSInfixExpression("=", getReference(obj),
MANAGED_MAGIC));
}
@Override
protected CSStatement marshalOut(ManagedVariable arg, ManagedVariable obj, ManagedVariable ptr) {
return null;
}
@Override
protected CSStatement nativeToManaged(ManagedVariable arg, ManagedVariable obj, ManagedVariable ptr) {
return null;
}
@Override
public CSStatement free(ManagedVariable obj) {
return createManagedAssert(new CSInfixExpression("!=", getReference(obj), MANAGED_MAGIC));
}
@Override
protected Statement unwrap(NativeVariable src, NativeVariable dest) {
return new AssignmentStatement(getReference(dest), NATIVE_MAGIC);
}
@Override
protected Statement freeMembers(NativeVariable obj) {
return createAssert(new BinaryOperator("==", getReference(obj), NATIVE_MAGIC));
}
@Override
protected Statement wrap(NativeVariable src, NativeVariable dest) {
return null;
}
@Override
protected Statement marshalOut(NativeVariable src, NativeVariable dest) {
return null;
}
}
}