package sharpen.xobotos.generator; import static sharpen.core.framework.Environments.my; import org.eclipse.jdt.core.dom.*; import sharpen.core.CSharpBuilder; import sharpen.core.Configuration.MemberMapping; import sharpen.core.Configuration.TypeMapping; import sharpen.core.Sharpen; import sharpen.core.SharpenConversion; import sharpen.core.csharp.ast.*; import sharpen.core.framework.ASTResolver; import sharpen.core.framework.ASTUtility; import sharpen.core.framework.BindingUtils; import sharpen.core.framework.CSharpDriver; import sharpen.xobotos.StandardConfiguration.ConfigFlags; import sharpen.xobotos.XobotConfiguration; import sharpen.xobotos.api.APIDefinition; import sharpen.xobotos.api.Visibility; import sharpen.xobotos.api.bindings.*; import sharpen.xobotos.api.bindings.MethodBinding; import sharpen.xobotos.api.bindings.VariableBinding; import sharpen.xobotos.api.interop.NativeHandle; import sharpen.xobotos.api.interop.NativeMethodBuilder; import sharpen.xobotos.api.interop.Signature; import sharpen.xobotos.api.interop.Signature.Mode; import sharpen.xobotos.api.interop.Signature.ParameterInfo; import sharpen.xobotos.api.interop.marshal.MarshalAsClass; import sharpen.xobotos.api.templates.*; import sharpen.xobotos.output.IOutputProvider; import sharpen.xobotos.output.OutputMode; import sharpen.xobotos.output.OutputType; import java.util.Stack; public abstract class SharpenGenerator implements CSharpDriver { private final CompilationUnitBuilder _builder; private final Stack<ITypeBuilder> _typeStack = new Stack<ITypeBuilder>(); private final Stack<IOutputProvider> _outputProviderStack = new Stack<IOutputProvider>(); private AbstractMethodBuilder<?, ?> _currentMethod; public SharpenGenerator(CompilationUnitBuilder builder, IOutputProvider defaultOutput) { this._builder = builder; if (defaultOutput != null) _outputProviderStack.push(defaultOutput); _outputProviderStack.push(builder.getTemplate()); } public boolean generate() { XobotConfiguration config = my(XobotConfiguration.class); ConfigFlags flags = _builder.getTemplate().getConfigFlags(); if (flags != null) config = config.clone(flags); XobotSharpenConversion conversion = new XobotSharpenConversion(config); if (!conversion.convert()) return false; if (getOutputType().removeDocs()) _builder.getCompilationUnit().clearComments(); return true; } protected abstract void registerType(ITypeBinding binding, CSTypeDeclaration type); protected abstract void registerMethod(IMethodBinding binding, CSMethod method); private class XobotSharpenConversion extends SharpenConversion { protected XobotSharpenConversion(XobotConfiguration config) { super(config); setSource(_builder.getPair().source); setDriver(SharpenGenerator.this); setASTResolver(my(ASTResolver.class)); } public boolean convert() { final CompilationUnit ast = _builder.getPair().ast; final String path = ASTUtility.compilationUnitPath(ast); prepareForConversion(ast); try { convert(_builder.getPair(), _builder.getCompilationUnit()); return true; } catch (Exception e) { Sharpen.Log(e, "Exception while converting %s", path); createProblemMarker(ast, null, "Exception: " + e.toString(), true); _foundErrors = true; return false; } } } private CSTypeDeclaration generateNakedStub(CSharpBuilder builder, TypeDeclaration node, CSTypeDeclaration type) { type.addAttribute(new CSAttribute("Sharpen.NakedStub")); type.visibility(CSVisibility.Public); type.removeDocs(); if (node.isInterface()) return type; for (final Object member : node.bodyDeclarations()) { if (!(member instanceof TypeDeclaration)) continue; TypeDeclaration nested = (TypeDeclaration) member; if (Modifier.isPrivate(nested.getModifiers())) continue; CSTypeDeclaration inner = builder.mapTypeDeclaration(nested); CSTypeDeclaration innerType = generateNakedStub(builder, nested, inner); type.addMember(innerType); } return type; } protected ITypeContainer getCurrentContainer() { if (_typeStack.size() > 0) return _typeStack.peek(); else return _builder.getTemplate(); } private TypeTemplate findTypeTemplate(TypeDeclaration node) { final ITypeContainer container = getCurrentContainer(); return container.findTypeTemplate(node); } private EnumTemplate findEnumTemplate(EnumDeclaration node) { final ITypeContainer container = getCurrentContainer(); return container.findEnumTemplate(node); } private OutputType getOutputType() { for (int i = _outputProviderStack.size() - 1; i >= 0; i--) { IOutputProvider provider = _outputProviderStack.get(i); if (provider == null) continue; OutputType type = provider.getOutputType(); if (type != null) return type; } return OutputType.NAKED_STUB; } @Override public CSTypeDeclaration processTypeDeclaration(CSharpBuilder csharpBuilder, CSTypeContainer parent, TypeDeclaration node, ITypeBuilderDelegate delegate) { final TypeTemplate template = findTypeTemplate(node); if (template == null) return null; final ITypeBinding typeBinding = node.resolveBinding(); final AbstractTypeBinding binding = my(BindingManager.class).resolveBinding(typeBinding); if ((binding != null) && (binding.getMapping() != null)) return null; try { _outputProviderStack.push(template); final OutputType output = getOutputType(); final OutputMode mode; if (node.resolveBinding().isNested()) mode = output.getModeForMember(node); else mode = output.getMode(); CSTypeDeclaration type; if (mode == OutputMode.NOTHING) return null; else if (mode == OutputMode.NAKED_STUB) { type = delegate.create(); generateNakedStub(csharpBuilder, node, type); parent.addType(type); return type; } TypeBuilder builder = new TypeBuilder(template, output, node); _typeStack.push(builder); type = builder.build(csharpBuilder, delegate); if (type != null) { registerType(typeBinding, type); parent.addType(type); } _typeStack.pop(); return type; } finally { _outputProviderStack.pop(); } } @Override public CSMethodBase processMethodDeclaration(CSharpBuilder csharpBuilder, CSTypeDeclaration parent, MethodDeclaration node, IMethodBuilderDelegate delegate) { final ITypeBuilder type = _typeStack.peek(); final AbstractMethodTemplate<?> template = type.findMethodTemplate(node); if (template == null) return null; try { _outputProviderStack.push(template); final OutputType output = getOutputType(); final OutputMode mode = output.getModeForMember(node); if (mode == OutputMode.NOTHING) return null; MethodBinding binding = template.getBinding(); if ((binding != null) && (binding.getNativeHandle() != null)) return null; AbstractMethodBuilder<?, ?> builder; if (node.isConstructor()) builder = new ConstructorBuilder((ConstructorTemplate) template, output, node); else if (template instanceof DestructorTemplate) builder = new DestructorBuilder((DestructorTemplate) template, output, node); else builder = new MethodBuilder(type, (MethodTemplate) template, output, node, _builder.getNativeBuilder()); type.registerMember(node, builder); _currentMethod = builder; CSMethodBase method = builder.build(csharpBuilder, delegate); _currentMethod = null; if (method != null) { delegate.fixup(parent, method); parent.addMember(method); if (method instanceof CSMethod) registerMethod(node.resolveBinding(), (CSMethod) method); } return method; } finally { _outputProviderStack.pop(); } } @Override public CSField processFieldDeclaration(CSharpBuilder csharpBuilder, CSTypeDeclaration parent, FieldDeclaration node, VariableDeclarationFragment fragment, IFieldBuilderDelegate delegate) { final ITypeBuilder type = _typeStack.peek(); final FieldTemplate template = type.getTypeTemplate().findFieldTemplate(node); if (template == null) return null; try { _outputProviderStack.push(template); final OutputType output = getOutputType(); final OutputMode mode = output.getModeForMember(node); if (mode == OutputMode.NOTHING) return null; FieldBuilder builder = new FieldBuilder(template, output, node, fragment); type.registerMember(node, builder); CSField field = builder.build(csharpBuilder, delegate); if (field != null) parent.addMember(field); return field; } finally { _outputProviderStack.pop(); } } @Override public CSAnonymousClass processAnonymousClass(CSharpBuilder csharpBuilder, CSTypeContainer parent, AnonymousClassDeclaration node, IAnonymousClassBuilderDelegate delegate) { final TypeTemplate typeTemplate = _typeStack.peek().getTypeTemplate(); final AnonymousClassTemplate template = typeTemplate.findAnonymousClassTemplate(node); if (template == null) return null; try { _outputProviderStack.push(template); final OutputType output = getOutputType(); final OutputMode mode = output.getModeForMember(node); if (mode == OutputMode.NOTHING) return null; AnonymousClassBuilder builder = new AnonymousClassBuilder(template, output, node); _typeStack.push(builder); CSAnonymousClass type = builder.build(csharpBuilder, delegate); if (type != null) parent.addType(type.type()); _typeStack.pop(); return type; } finally { _outputProviderStack.pop(); } } @Override public CSProperty processPropertyDeclaration(CSharpBuilder csharpBuilder, CSTypeDeclaration parent, MethodDeclaration node, String name, CSProperty property, IPropertyBuilderDelegate delegate) { final ITypeBuilder type = _typeStack.peek(); final PropertyTemplate template = type.getTypeTemplate().findPropertyTemplate(node); if (template == null) return null; try { _outputProviderStack.push(template); final OutputType output = getOutputType(); final OutputMode mode = output.getModeForMember(node); if (mode == OutputMode.NOTHING) return null; PropertyBuilder builder = new PropertyBuilder(template, output, node); type.registerMember(node, builder); boolean existing = property != null; property = builder.build(csharpBuilder, delegate); if (!existing) parent.addMember(property); return property; } finally { _outputProviderStack.pop(); } } @Override public CSEnum processEnumDeclaration(CSharpBuilder csharpBuilder, CSTypeContainer parent, EnumDeclaration node, IEnumBuilderDelegate delegate) { final EnumTemplate template = findEnumTemplate(node); if (template == null) return null; try { _outputProviderStack.push(template); final OutputType output = getOutputType(); final OutputMode mode; if (node.resolveBinding().isNested()) mode = output.getModeForMember(node); else mode = output.getMode(); if (mode == OutputMode.NOTHING) return null; EnumBuilder builder = new EnumBuilder(template, output, node); CSEnum theEnum = builder.build(csharpBuilder, delegate); if (theEnum != null) parent.addType(theEnum); return theEnum; } finally { _outputProviderStack.pop(); } } @Override public CSTypeDeclaration processExtractedEnumDeclaration(CSharpBuilder csharpBuilder, CSTypeContainer parent, EnumDeclaration node, ITypeBuilderDelegate delegate) { final EnumTemplate template = findEnumTemplate(node); if (template == null) return null; final ITypeBinding typeBinding = node.resolveBinding(); final AbstractTypeBinding binding = my(BindingManager.class).resolveBinding(typeBinding); if ((binding != null) && (binding.getMapping() != null)) return null; try { _outputProviderStack.push(template); final OutputType output = getOutputType(); final OutputMode mode; if (node.resolveBinding().isNested()) mode = output.getModeForMember(node); else mode = output.getMode(); CSTypeDeclaration type; if ((mode == OutputMode.NOTHING) || (mode == OutputMode.NAKED_STUB)) return null; ExtractedEnumBuilder builder = new ExtractedEnumBuilder(template, output, node); _typeStack.push(builder); type = builder.build(csharpBuilder, delegate); if (type != null) parent.addType(type); _typeStack.pop(); return type; } finally { _outputProviderStack.pop(); } } @Override public String mappedVariableName(IVariableBinding binding) { VariableBinding variable = my(BindingManager.class).resolveBinding(binding); if (variable == null) return null; return variable.rename(); } @Override public CSExpression mappedNullPointer(IVariableBinding binding) { if (binding == null) return null; VariableBinding variable = my(BindingManager.class).resolveBinding(binding); if (variable == null) return null; if (variable.getNativeHandle() != null) return new CSNullLiteralExpression(); if (variable.isPointer()) return new CSReferenceExpression("System.IntPtr.Zero"); return null; } private VariableBinding lookupVariableBinding(Expression expr) { if (expr instanceof Name) { IBinding binding = ((Name) expr).resolveBinding(); if (binding instanceof IVariableBinding) return my(BindingManager.class).resolveBinding((IVariableBinding) binding); return null; } if (expr instanceof QualifiedName) { IBinding binding = ((QualifiedName) expr).resolveBinding(); if (binding instanceof IVariableBinding) return my(BindingManager.class).resolveBinding((IVariableBinding) binding); return null; } return null; } @Override public CSExpression mappedNullPointer(Expression expr) { VariableBinding variable = lookupVariableBinding(expr); if (variable != null) { if (variable.getNativeHandle() != null) return new CSNullLiteralExpression(); if (variable.isPointer()) return new CSReferenceExpression("System.IntPtr.Zero"); return null; } if (expr instanceof MethodInvocation) { IMethodBinding method = ((MethodInvocation) expr).resolveMethodBinding(); MethodBinding binding = my(BindingManager.class).resolveBinding(method); if (binding == null) return null; if (binding.getNativeHandle() != null) return new CSNullLiteralExpression(); } return null; } @Override public CSTypeReferenceExpression mappedVariableType(IVariableBinding binding) { if (binding == null) return null; VariableBinding variable = my(BindingManager.class).resolveBinding(binding); if (variable == null) return null; NativeHandle nh = variable.getNativeHandle(); if (nh != null) return nh.getManagedType(); if (variable.isPointer()) return new CSTypeReference("System.IntPtr"); if (variable.modifyType() != null) return variable.modifyType().getExpression(); return null; } @Override public MemberMapping mappedMethod(IMethodBinding binding) { MethodBinding method = my(BindingManager.class).resolveBinding(binding); if (method != null) return method.getMapping(); return null; } @Override public CSExpression mappedMethodInvocation(CSharpBuilder builder, MethodInvocation node) { final IMethodBinding binding = node.resolveMethodBinding(); final ITypeBinding declaringType = binding.getDeclaringClass(); final AbstractTypeBinding typeBinding = my(BindingManager.class).resolveBinding(declaringType); final MethodBinding methodBinding = my(BindingManager.class).resolveBinding(binding); if (typeBinding instanceof EnumBinding) { EnumBinding enumBinding = (EnumBinding) typeBinding; String ctorMethod = enumBinding.getConstructorMethod(); if ((ctorMethod != null) && node.getName().toString().equals(ctorMethod)) { return builder.mapExpression(declaringType, (Expression) node.arguments().get(0)); } } if (methodBinding != null) { NativeHandle nh = methodBinding.getNativeHandle(); if (nh != null) { CSExpression expr = builder.mapExpression(declaringType, node.getExpression()); String member = nh.getProperty() != null ? nh.getProperty() : nh.getField(); return new CSMemberReferenceExpression(expr, member); } } if (_currentMethod != null) _currentMethod.checkInvocationTarget(binding); return null; } @Override public CSExpression mappedMethodInvocationArgument(CSharpBuilder builder, MethodInvocation node, int index, Expression expr) { final IMethodBinding binding = node.resolveMethodBinding(); final NativeMethodBuilder nativeBuilder = my(BindingManager.class).resolveNativeBinding(binding); final ITypeBinding[] actualTypes = binding.getParameterTypes(); final CSExpression mappedExpr = builder.mapExpression(actualTypes[index], expr); if (nativeBuilder != null) { final Signature signature = nativeBuilder.getNativeMethod().getSignature(); final ParameterInfo info = signature.getParameterInfo(index); if ((info == null) || (info.mode == Mode.REMOVE)) return null; if ((info.marshal == null) || !(info.marshal instanceof MarshalAsClass.Entry)) return mappedExpr; if (CSharpBuilder.isZeroLiteral(expr)) { MarshalAsClass.Entry marshal = (MarshalAsClass.Entry) info.marshal; if (marshal.getNativeHandle() != null) return new CSNullLiteralExpression(); else return new CSReferenceExpression("System.IntPtr.Zero"); } } return mappedExpr; } @Override public CSExpression mappedEnumAccess(CSharpBuilder builder, Expression expr, IVariableBinding binding) { final ITypeBinding declaringType = binding.getDeclaringClass(); final AbstractTypeBinding typeBinding = my(BindingManager.class).resolveBinding(declaringType); if ((typeBinding == null) || !(typeBinding instanceof EnumBinding)) return null; EnumBinding enumBinding = (EnumBinding) typeBinding; if ((enumBinding.getValueField() != null) && (binding.getName().equals(enumBinding.getValueField()))) return builder.mapExpression(binding.getType(), expr); if (!Modifier.isStatic(binding.getModifiers())) return null; CSTypeReferenceExpression qualifier = new CSTypeReference(BindingUtils.qualifiedName(declaringType)); return new CSMemberReferenceExpression(qualifier, binding.getName()); } @Override public CSExpression castIfNeeded(CSharpBuilder builder, ITypeBinding expectedType, ITypeBinding actualType, CSExpression expression) { final AbstractTypeBinding expectedBinding = my(BindingManager.class).resolveBinding(expectedType); final AbstractTypeBinding actualBinding = my(BindingManager.class).resolveBinding(actualType); if ((actualBinding instanceof EnumBinding) && expectedType.isPrimitive()) { final EnumBinding enumBinding = (EnumBinding) actualBinding; final String baseType = enumBinding.getBaseType(); if ((baseType != null) && expectedType.getName().equals(baseType)) return new CSCastExpression(new CSTypeReference(baseType), expression); } if ((expectedBinding instanceof EnumBinding) && actualType.isPrimitive()) { final EnumBinding enumBinding = (EnumBinding) expectedBinding; final String baseType = enumBinding.getBaseType(); if ((baseType != null) && actualType.getName().equals(baseType)) { CSTypeReferenceExpression tr = builder.mappedTypeReference(expectedType); return new CSCastExpression(tr, expression); } } return null; } @Override public String mappedTypeName(CSharpBuilder builder, ITypeBinding binding) { final AbstractTypeBinding type = my(BindingManager.class).resolveBinding(binding); if (type != null) { TypeMapping mapping = type.getMapping(); if (mapping != null) return mapping.name; } return null; } @Override public CSTypeReferenceExpression mappedTypeReference(CSharpBuilder builder, ITypeBinding binding) { final AbstractTypeBinding type = my(BindingManager.class).resolveBinding(binding); if (type instanceof EnumBinding) { final EnumBinding enumBinding = (EnumBinding) type; if (enumBinding.isNullable()) { CSTypeReference tr = new CSTypeReference(BindingUtils.qualifiedName(binding)); return new CSNullableTypeReference(tr); } } return null; } private CSVisibility mapVisibility(MemberBinding binding) { Visibility visibility = binding.getVisibility(); if (visibility != null) return APIDefinition.mapVisibility(visibility); return null; } @Override public CSVisibility mapVisibility(ITypeBinding binding) { final AbstractTypeBinding typeBinding = my(BindingManager.class).resolveBinding(binding); if (typeBinding == null) return null; return mapVisibility(typeBinding); } @Override public CSVisibility mapVisibility(IMethodBinding binding) { final MethodBinding methodBinding = my(BindingManager.class).resolveBinding(binding); if (methodBinding == null) return null; return mapVisibility(methodBinding); } @Override public CSVisibility mapVisibility(IVariableBinding binding) { final VariableBinding variableBinding = my(BindingManager.class).resolveBinding(binding); if (variableBinding == null) return null; return mapVisibility(variableBinding); } }