package sharpen.xobotos.api.interop; import static sharpen.core.framework.Environments.my; import org.eclipse.jdt.core.dom.ITypeBinding; import org.eclipse.jdt.core.dom.IVariableBinding; import sharpen.core.Configuration; import sharpen.core.NamingStrategy; import sharpen.core.Sharpen; import sharpen.core.csharp.ast.*; import sharpen.core.framework.BindingUtils; import sharpen.xobotos.api.bindings.BindingManager; import sharpen.xobotos.api.interop.NativeMethodBuilder.ElementInfo; import sharpen.xobotos.api.interop.NativeStruct.MemberInfo; 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.marshal.MarshalInfo; import java.util.List; import java.util.logging.Level; public class StructHelperClass extends HelperClassBuilder { private final NativeStruct _template; private boolean _resolved; private boolean _resolveFailed; private ElementInfo[] _fieldInfo; public StructHelperClass(NativeBuilder builder, ITypeBinding type, NativeStruct template) { super(type, type.getName(), builder, new CSTypeReference(BindingUtils.qualifiedName(type)), template.getNativeType(), true, false); this._template = template; this._resolved = false; } @Override protected boolean isByRef() { return false; } @Override public List<String> getIncludes() { return _template.getIncludes(); } @Override protected CSTypeReferenceExpression getManagedHelperType() { return new CSNestedTypeReference(getManagedType(), super.getManagedHelperType()); } @Override protected boolean isBlittable() { return false; } @Override protected void buildMembers() { if (!_resolved) throw new IllegalStateException(); if (_resolveFailed) return; final NamingStrategy ns = my(Configuration.class).getNamingStrategy(); final List<MemberInfo> members = _template.getMembers(); for (int i = 0; i < members.size(); i++) { final MemberInfo member = members.get(i); final String name = ns.identifier(member.getName()); if (member.getValue() != null) addMember(new ValueMember(name, member.getValue())); else addMember(new StructMember(_fieldInfo[i], member.getNativeName(), name, member.getMode())); } } @Override public boolean resolve(IMarshalContext context) { if (BindingManager.DEBUG) Sharpen.Debug("RESOLVE STRUCT: %s", getName()); if (_resolved) return !_resolveFailed; final List<MemberInfo> members = _template.getMembers(); _fieldInfo = new ElementInfo[members.size()]; for (int i = 0; i < members.size(); i++) { final MemberInfo member = members.get(i); IVariableBinding field = findField(getType(), member.getName()); if (field == null) { Sharpen.Log(Level.SEVERE, "No such field '%s' in type '%s'", member.getName(), BindingUtils.qualifiedName(getType())); _resolveFailed = true; continue; } if (member.getValue() != null) continue; MarshalInfo marshal; if (member.getMarshalInfo() != null) marshal = member.getMarshalInfo().resolve(field.getType()); else marshal = context.getMarshalInfo(field.getType()); if (marshal == null) { Sharpen.Log(Level.SEVERE, "Missing marshal info for type '%s' of field '%s'", BindingUtils.qualifiedName(field.getType()), member.getName()); _resolveFailed = true; continue; } _fieldInfo[i] = new ElementInfo(field.getType(), marshal, null, Flags.ELEMENT); if (BindingManager.DEBUG) Sharpen.Debug("STRUCT MEMBER: %s - %s", member.getName(), BindingUtils.qualifiedName(field.getType())); if (!marshal.resolve(context, field.getType())) { Sharpen.Log(Level.SEVERE, "Failed to resolve field '%s' in '%s'", member.getName(), BindingUtils.qualifiedName(getType())); _resolveFailed = true; continue; } if (!marshal.isPrimitiveType()) { Sharpen.Log(Level.SEVERE, "Field '%s' in '%s' is not a primitive type", member.getName(), BindingUtils.qualifiedName(getType())); _resolveFailed = true; continue; } if (BindingManager.DEBUG) Sharpen.Debug("STRUCT MEMBER DONE: %s - %s", member.getName(), BindingUtils.qualifiedName(field.getType())); } if (BindingManager.DEBUG) Sharpen.Debug("RESOLVE STRUCT DONE: %s%s", getName(), _resolveFailed ? " - ERROR" : ""); _resolved = true; return true; } private IVariableBinding findField(ITypeBinding type, String name) { for (final IVariableBinding field : type.getDeclaredFields()) { if (field.getName().equals(name)) return field; } return null; } @Override protected CSExpression computeNativeSize(CSExpression expr) { return new CSMethodInvocationExpression(new CSReferenceExpression("Marshal.SizeOf"), new CSTypeofExpression(getManagedStructType())); } @Override protected CSExpression createInstance(ManagedVariable obj) { return new CSConstructorInvocationExpression(getManagedType()); } private static class StructMember extends ElementMember { private final String _ptrName; private final Mode _mode; private StructMember(ElementInfo info, String nativeName, String managedName, Mode mode, Flags... flags) { super(info, nativeName, managedName, flags); this._ptrName = "ptr_" + nativeName; this._mode = mode; } private Mode getMode() { return _mode; } @Override protected Statement unwrap(NativeVariable src, NativeVariable dest) { if (!isClass()) return new AssignmentStatement(getReference(dest), getReference(src)); return new AssignmentStatement(getReference(dest), new MethodInvocation( getHelper().UNWRAP.expr(), getReference(src))); } @Override protected void createPinnedPtr(CSStruct struct, CSMethod free) { if (!isClass()) return; CSField ptr = new CSField(_ptrName, new CSTypeReference("System.IntPtr"), CSVisibility.Public); struct.addMember(ptr); free.body().addStatement( new CSMethodInvocationExpression(getHelper().FREE_MANAGED_PTR.expr(), new CSReferenceExpression(_ptrName))); } @Override protected CSStatement getPinnedPtr(ManagedVariable arg, ManagedVariable obj, ManagedVariable pinned) { CSExpression expr = getReference(arg); if (isClass()) { expr = new CSMethodInvocationExpression(getHelper().MANAGED_TO_NATIVE.expr(), expr); CSBlock block = new CSBlock(); block.addStatement(new CSInfixExpression("=", getReference(obj), expr)); block.addStatement(new CSInfixExpression("=", new CSMemberReferenceExpression(pinned.getReference(), _ptrName), getReference(obj))); return block; } else { return new CSExpressionStatement(-1, new CSInfixExpression("=", getReference(obj), expr)); } } @Override protected CSStatement marshalIn(ManagedVariable arg, ManagedVariable obj, ManagedVariable ptr) { CSExpression expr; if (isClass()) { expr = new CSMethodInvocationExpression(getHelper().MANAGED_TO_NATIVE.expr(), getReference(arg)); } else { expr = getElementInfo().marshalIn(getReference(arg)); } return new CSExpressionStatement(-1, new CSInfixExpression("=", getReference(obj), expr)); } @Override protected CSStatement marshalOut(ManagedVariable arg, ManagedVariable obj, ManagedVariable ptr) { if (getMode() == Mode.IN) return null; CSExpression expr = getReference(obj); if (isClass()) { expr = new CSMethodInvocationExpression(getHelper().NATIVE_TO_MANAGED.expr(), expr); } return new CSExpressionStatement(-1, new CSInfixExpression("=", getReference(arg), expr)); } @Override protected CSStatement nativeToManaged(ManagedVariable arg, ManagedVariable obj, ManagedVariable ptr) { if (getMode() == Mode.IN) return null; CSExpression expr = getReference(obj); if (isClass()) { expr = new CSMethodInvocationExpression(getHelper().NATIVE_TO_MANAGED.expr(), expr); } return new CSExpressionStatement(-1, new CSInfixExpression("=", getReference(arg), expr)); } @Override public CSStatement free(ManagedVariable obj) { if (!isClass()) return null; return new CSExpressionStatement(-1, new CSMethodInvocationExpression(getHelper().FREE_MANAGED_PTR.expr(), getReference(obj))); } @Override public Statement freeMembers(NativeVariable obj) { if (!isClass()) return null; return new ExpressionStatement(new MethodInvocation(getHelper().DESTRUCTOR.expr(), getReference(obj))); } @Override protected Statement wrap(NativeVariable src, NativeVariable dest) { if (!isClass()) { Expression expr = getElementInfo().marshalNativeArg(getReference(src)); return new AssignmentStatement(getReference(dest), expr); } else if (isByRef()) return new AssignmentStatement(getReference(dest), new MethodInvocation( getHelper().WRAP.expr(), getReference(src))); else { Expression destAddr = new AddressOfExpression(getReference(dest)); return new ExpressionStatement(new MethodInvocation(getHelper().WRAP.expr(), getReference(src), destAddr)); } } @Override protected Statement marshalOut(NativeVariable src, NativeVariable dest) { if (getMode() == Mode.IN) return null; if (isClass()) { Expression srcAddr = new DereferenceExpression(getReference(src)); return new ExpressionStatement(new MethodInvocation(getHelper().MARSHAL_OUT.expr(), srcAddr, getReference(dest))); } else { return new AssignmentStatement(getReference(dest), getReference(src)); } } } private static class ValueMember extends Member { private final String _value; public ValueMember(String name, String value) { super(null, null, null, name, Flags.HAS_VALUE); this._value = value; } @Override protected void createPinnedPtr(CSStruct struct, CSMethod dtor) { ; } @Override protected CSStatement getPinnedPtr(ManagedVariable arg, ManagedVariable obj, ManagedVariable pinned) { return null; } @Override protected CSStatement marshalIn(ManagedVariable arg, ManagedVariable obj, ManagedVariable ptr) { return null; } @Override protected CSStatement marshalOut(ManagedVariable arg, ManagedVariable obj, ManagedVariable ptr) { return new CSExpressionStatement(-1, new CSInfixExpression("=", getReference(arg), new CSStringLiteralExpression(_value))); } @Override protected CSStatement nativeToManaged(ManagedVariable arg, ManagedVariable obj, ManagedVariable ptr) { return new CSExpressionStatement(-1, new CSInfixExpression("=", getReference(arg), new CSStringLiteralExpression(_value))); } @Override public CSStatement free(ManagedVariable obj) { return null; } @Override protected Statement wrap(NativeVariable src, NativeVariable dest) { return null; } @Override protected Statement unwrap(NativeVariable src, NativeVariable dest) { return null; } @Override protected Statement freeMembers(NativeVariable obj) { return null; } @Override protected Statement marshalOut(NativeVariable src, NativeVariable dest) { return null; } } }