package sharpen.xobotos.api.interop;
import org.eclipse.jdt.core.dom.IVariableBinding;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.VariableDeclaration;
import sharpen.core.framework.ByRef;
import sharpen.xobotos.api.interop.NativeMethod.Kind;
import sharpen.xobotos.api.interop.NativeMethodBuilder.ElementInfo;
import sharpen.xobotos.api.interop.glue.*;
import sharpen.xobotos.api.interop.glue.AbstractMember.Visibility;
import sharpen.xobotos.api.interop.glue.Method.Flags;
import sharpen.xobotos.api.interop.marshal.MarshalAsNativeType;
import java.util.ArrayList;
import java.util.List;
public class NativeGlueGenerator {
private final NativeMethodBuilder _builder;
private final NativeMethod _method;
private final MethodDeclaration _decl;
public NativeGlueGenerator(NativeMethodBuilder builder) {
this._builder = builder;
this._method = builder.getNativeMethod();
this._decl = builder.getMethod();
}
private ElementInfo _returnInfo;
private AbstractNativeTypeBuilder _returnHelper;
private AbstractTypeReference _returnType;
private ElementInfo[] _paramInfo;
private IVariableBinding _implicitInstance;
private String _nativeName;
private String _nativeFunction;
private AbstractTypeReference _nativeClass;
protected boolean resolve() {
_returnInfo = _builder.getReturnInfo();
_paramInfo = _builder.getParameterInfo();
_nativeName = _builder.getNativeName();
_nativeFunction = _builder.getNativeFunction();
_implicitInstance = _builder.getImplicitInstance();
if (_method.getNativeClass() != null)
_nativeClass = new TypeReference(_method.getNativeClass());
else if (_builder.getNativeHandle() != null)
_nativeClass = _builder.getNativeHandle().getNativeClass();
if (_returnInfo != null) {
if (_returnInfo.marshal instanceof MarshalAsNativeType) {
_returnHelper = ((MarshalAsNativeType) _returnInfo.marshal).getTypeBuilder();
_returnType = new PointerType(_returnHelper.getNativePInvokeType());
} else
_returnType = _returnInfo.getNativeType();
} else {
_returnType = new TypeReference("void");
}
return true;
}
protected boolean generate(Printer printer) {
Method method = generate();
if (method == null)
return false;
printer.visit(method);
return true;
}
public Method generate() {
final Method method = new Method(_nativeFunction, _returnType, Visibility.PUBLIC, Flags.EXPORT);
final List<Expression> args = new ArrayList<Expression>();
final List<Statement> preStmnts = new ArrayList<Statement>();
final List<Statement> postStmnts = new ArrayList<Statement>();
final Kind kind = _method.getKind();
final ByRef<Expression> instanceArg;
if ((kind == Kind.INSTANCE) || (kind == Kind.PROXY))
instanceArg = new ByRef<Expression>(null);
else
instanceArg = null;
if (_implicitInstance != null) {
Parameter param = new Parameter(new PointerType(_nativeClass), "_instance");
method.addParameter(param);
Expression arg = new VariableReference(param);
if (instanceArg != null)
instanceArg.value = arg;
else
args.add(new DereferenceExpression(arg));
}
for (int i = 0; i < _paramInfo.length; i++) {
if (_paramInfo[i] == null)
continue;
VariableDeclaration vdecl = (VariableDeclaration) _decl.parameters().get(i);
IVariableBinding vbinding = vdecl.resolveBinding();
final String name = getNativeName(vbinding.getName());
boolean hasInstanceArg = (instanceArg != null) && (instanceArg.value == null);
final boolean isInstanceArg = hasInstanceArg && (i == 0);
INativeMarshalContext context = new INativeMarshalContext() {
@Override
public void addPreStatement(Statement statement) {
preStmnts.add(statement);
}
@Override
public void addPostStatement(Statement statement) {
postStmnts.add(statement);
}
@Override
public NativeVariable createParameter(String suffix, AbstractTypeReference type) {
String pname = suffix != null ? name + "_" + suffix : name;
Parameter param = new Parameter(type, pname);
method.addParameter(param);
return new NativeVariable(param);
}
@Override
public NativeVariable createVariable(String suffix, AbstractTypeReference type,
Expression init) {
String vname = suffix != null ? name + "_" + suffix : name;
LocalVariable var = new LocalVariable(type, vname, init);
preStmnts.add(var);
return new NativeVariable(var);
}
@Override
public void addArgument(Expression arg) {
if (isInstanceArg)
instanceArg.value = arg;
else
args.add(arg);
}
};
_paramInfo[i].marshal.marshalNative(context, _paramInfo[i].mode, _paramInfo[i].flags);
}
if (_method.getKind() == Kind.DESTRUCTOR) {
method.getBody().addStatement(new DestructorInvocation(args.get(0)));
return method;
}
final AbstractInvocation invocation;
if (_method.getKind() == Kind.INSTANCE) {
Expression member = new InstanceMemberAccess(instanceArg.value, _nativeName);
invocation = new MethodInvocation(member, args);
} else if (_method.getKind() == Kind.PROXY) {
Expression member = new StaticMemberAccess(_nativeClass, _nativeName);
args.add(0, new DereferenceExpression(instanceArg.value));
invocation = new MethodInvocation(member, args);
} else if (_method.getKind() == Kind.STATIC) {
Expression member = new StaticMemberAccess(_nativeClass, _nativeName);
invocation = new MethodInvocation(member, args);
} else if (_method.getKind() == Kind.CONSTRUCTOR) {
AbstractTypeReference type = ((PointerType) _returnType).getElementType();
invocation = new ConstructorInvocation(type, args);
} else {
return null;
}
for (Statement stm : preStmnts) {
method.getBody().addStatement(stm);
}
final NativeVariable retval;
if (_returnHelper != null) {
NativeVariable cppRetval = NativeVariable.createLocal(method.getBody(),
_returnHelper.getRealNativeType(), "_returnObj", invocation,
NativeVariable.Flags.BYREF);
retval = NativeVariable.createLocal(method.getBody(),
_returnHelper.getNativePInvokeType(), "_retval",
_returnHelper.marshalNativeRetval(cppRetval.getReference()),
NativeVariable.Flags.BYREF);
method.getBody().addStatement(new DestructorInvocation(cppRetval.getReference()));
} else if (_returnInfo != null) {
retval = NativeVariable.createLocal(method.getBody(), _returnType, "_retval", invocation);
} else {
method.getBody().addStatement(invocation);
retval = null;
}
for (Statement stm : postStmnts) {
method.getBody().addStatement(stm);
}
if (retval != null) {
method.getBody().addStatement(new ReturnStatement(retval.getReference()));
}
return method;
}
private String getNativeName(String name) {
if (name.equals("mutable") || name.equals("namespace"))
return "_" + name;
return name;
}
}