/* * To change this template, choose Tools | Templates * and open the template in the editor. */ package com.ochafik.lang.jnaerator; import com.ochafik.io.IOUtils; import com.ochafik.lang.jnaerator.parser.DeclarationsHolder; import com.ochafik.lang.jnaerator.parser.Function; import com.ochafik.lang.jnaerator.parser.Identifier; import com.ochafik.lang.jnaerator.parser.Struct; import com.ochafik.lang.jnaerator.parser.VariablesDeclaration; import java.io.IOException; import com.ochafik.lang.jnaerator.parser.Arg; import com.ochafik.lang.jnaerator.parser.Declarator; import static com.ochafik.lang.jnaerator.parser.ElementsHelper.*; import com.ochafik.lang.jnaerator.parser.Expression; import com.ochafik.lang.jnaerator.parser.Include; import com.ochafik.lang.jnaerator.parser.ModifierType; import com.ochafik.lang.jnaerator.parser.SourceFile; import com.ochafik.lang.jnaerator.parser.TypeRef; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; import static com.ochafik.lang.jnaerator.GYPUtils.*; import com.ochafik.lang.jnaerator.TypeConversion.JavaPrim; import com.ochafik.lang.jnaerator.parser.Declaration; import com.ochafik.lang.jnaerator.parser.Define; import com.ochafik.lang.jnaerator.parser.Element; import com.ochafik.lang.jnaerator.parser.ElementsHelper; import com.ochafik.lang.jnaerator.parser.Expression.BinaryOperator; import com.ochafik.lang.jnaerator.parser.Expression.MemberRefStyle; import com.ochafik.lang.jnaerator.parser.Expression.UnaryOperator; import com.ochafik.lang.jnaerator.parser.Identifier.QualificationSeparator; import com.ochafik.lang.jnaerator.parser.Statement; import com.ochafik.lang.jnaerator.parser.Statement.Block; import com.ochafik.lang.jnaerator.parser.Statement.If; import com.ochafik.lang.jnaerator.parser.Statement.Return; import com.nativelibs4java.jalico.Pair; import java.io.File; import org.bridj.demangling.Demangler; /** * * @author ochafik */ public class NodeJSDeclarationsConverter extends DeclarationsConverter { public NodeJSDeclarationsConverter(Result result) { super(result); } @Override protected void configureCallbackStruct(Struct callbackStruct) { //throw new UnsupportedOperationException("Not supported yet."); } public void convertCallback(TypeRef.FunctionSignature functionSignature, Signatures signatures, DeclarationsHolder out, Identifier callerLibraryName) { } // public void convertConstants(String library, List<Define> defines, Element sourcesRoot, final Signatures signatures, final DeclarationsHolder out, final Identifier libraryClassName) { // } @Override protected void outputNSString(String name, String value, DeclarationsHolder out, Signatures signatures, Element... elementsToTakeCommentsFrom) { } @Override protected void convertConstant(String name, JavaPrim prim, TypeRef mutatedType, Expression defaultValue, VariablesDeclaration v, Declarator decl, DeclarationsHolder out, Signatures signatures, Identifier libraryClassName) { convertConstant(name, defaultValue, out, signatures); } Expression.Constant.Type getValueConstantType(Expression value) { if (value instanceof Expression.Cast) { return getValueConstantType(((Expression.Cast) value).getTarget()); } if (value instanceof Expression.BinaryOp) { return getValueConstantType(((Expression.BinaryOp) value).getFirstOperand()); } if (value instanceof Expression.Constant) { return ((Expression.Constant) value).getType(); } throw new UnsupportedConversionException(value, "Unknown value type"); } @Override protected void convertDefine(Define define, DeclarationsHolder out, Signatures signatures, Identifier libraryClassName) { Expression value = define.getValue(); convertConstant(define.getName(), value, out, signatures); } protected void convertConstant(String name, Expression value, DeclarationsHolder out, Signatures signatures) { Block initBlock = getInitMethodBody(out); Expression expr; Expression.Constant.Type valueType = getValueConstantType(value); switch (valueType) { case Int: case UInt: case IntegerString: case Long: case ULong: case LongString: case Short: case Byte: expr = methodCall(ident("v8", "Integer", "New"), value.clone()); break; case Double: case Float: expr = methodCall(ident("v8", "Number", "New"), value.clone()); break; case Bool: expr = methodCall(ident("v8", "Boolean", "New"), value.clone()); break; case Char: case String: expr = methodCall(ident("v8", "String", "New"), new Expression.Constant(valueType, value + "", null)); break; case Null: expr = methodCall(ident("v8", "Null")); break; default: throw new UnsupportedConversionException(value, "Constant type not supported yet"); } initBlock.addStatement(stat(methodCall(varRef(initTarget), MemberRefStyle.Arrow, "Set", methodCall(ident("v8", "String", "NewSymbol"), expr(name)), expr))); } Block getInitMethodBody(DeclarationsHolder decls) { for (Declaration decl : decls.getDeclarations()) { if (decl instanceof Function) { return ((Function) decl).getBody(); } } throw new RuntimeException("Init method not found in " + decls); } static Identifier ident(String... components) { return ElementsHelper.ident(Identifier.QualificationSeparator.Colons, components); } static Expression newV8String(String s) { return methodCall(ident("v8", "String", "New"), expr(s)); } static Expression arrowMethodCall(Expression target, String name, Expression... args) { return methodCall(target, Expression.MemberRefStyle.Arrow, name, args); } static final String argsName = "_arguments_"; static final String returnValueName = "_return_"; static final String scopeName = "_scope_"; static final String initTarget = "_target_"; static final String dummyNodeBufferFreeCallbackName = "_dummy_node_buffer_free_callback_"; private enum NodeType { Pointer, String, Number, Boolean, VAList, Unknown, Void }; private TypeRef resolveTypeDef(TypeRef typeRef) { TypeRef tr = result.typeConverter.normalizeTypeRef(typeRef); return tr; } private NodeType getNodeType(TypeRef tr) { if (tr instanceof TypeRef.TargettedTypeRef) { TypeRef.TargettedTypeRef ttr = (TypeRef.TargettedTypeRef) tr; if (ttr.getTarget().toString().matches("(const\\s+)?char")) { return NodeType.String; } else { return NodeType.Pointer; } } else if (tr instanceof TypeRef.SimpleTypeRef) { TypeRef.SimpleTypeRef str = (TypeRef.SimpleTypeRef) tr; String n = str + "";//strstr.getName().toString(); if (n.matches("bool|BOOL")) { return NodeType.Boolean; } else if (n.matches("void")) { return NodeType.Void; } else if (n.equals("va_list") || n.equals("__builtin_va_list")) { return NodeType.VAList; } else { return NodeType.Number; } } else if (tr instanceof TypeRef.FunctionSignature) { return NodeType.Pointer; } return NodeType.Unknown; } @Override protected void convertFunction(Function function, Signatures signatures, boolean callback, DeclarationsHolder declarations, DeclarationsHolder implementations, Identifier libraryClassName, String sig, Identifier functionName, String library, int iConstructor) { assert implementations == declarations || declarations == null; String methodName = library + "_" + (function.getParentElement() instanceof Struct ? ((Struct) function.getParentElement()).getTag() + "_" : "") + function.getName(); Function method = new Function(Function.Type.CppMethod, ident(methodName), typeRef(v8Ident("Handle", v8Ident("Value")))); method.addArg(new Arg(argsName, new TypeRef.Pointer(typeRef(v8Ident("Arguments")), Declarator.PointerStyle.Reference).addModifiers(ModifierType.Const))); Block body = new Block(); //if (args.Length() != 4) { //return THROW_ERROR_EXCEPTION("ffi_call() requires 4 arguments!"); List<Arg> args = function.getArgs(); int argCount = args.size(); body.addStatement( new If( expr(methodCall(varRef(argsName), "Length"), BinaryOperator.IsDifferent, expr(argCount)), new Return( methodCall(v8Ident("ThrowException"), newV8String(functionName + "() requires " + argCount + " arguments!"))))); List<Expression> params = new ArrayList<Expression>(); for (int iArg = 0; iArg < argCount; iArg++) { Arg arg = args.get(iArg); if (arg.isVarArg()) { throw new UnsupportedConversionException(function, "varargs not supported yet"); } TypeRef tr = resolveTypeDef(arg.getValueType()); NodeType argNodeType = getNodeType(tr); Expression typeTest = null; String typeErrorMessage = null; Expression argExpr = new Expression.ArrayAccess(varRef(argsName), expr(iArg)); Expression argDeclExpr = null; TypeRef argType = null; Expression argUsageExpr = null; switch (argNodeType) { case String: typeTest = arrowMethodCall(argExpr, "IsString"); typeErrorMessage = "expected a String"; argDeclExpr = arrowMethodCall(argExpr.clone(), "ToString"); argType = typeRef(v8Ident("Handle", v8Ident("String"))); argUsageExpr = cast(transformTypeForCast(arg.getValueType()), expr(UnaryOperator.Dereference, methodCall(ident("v8", "String", "AsciiValue"), varRef(arg.getName())))); break; case Pointer: typeTest = expr( methodCall(argExpr.clone(), MemberRefStyle.Arrow, "IsNull"), BinaryOperator.Or, methodCall(ident("node", "Buffer", "HasInstance"), argExpr)); typeErrorMessage = "expected a Buffer"; argDeclExpr = new Expression.ConditionalExpression( methodCall(argExpr.clone(), MemberRefStyle.Arrow, "IsNull"), varRef("NULL"), methodCall(ident("node", "Buffer", "Data"), methodCall(argExpr.clone(), Expression.MemberRefStyle.Dot, templateIdent(ident("As"), varRef(v8Ident("Object")))))); argType = new TypeRef.Pointer(typeRef(ident("char")), Declarator.PointerStyle.Pointer); argUsageExpr = cast(transformTypeForCast(arg.getValueType()), varRef(arg.getName())); break; case Boolean: case Number: String t = argNodeType.name(); typeTest = arrowMethodCall(argExpr, "Is" + t); typeErrorMessage = "expected a " + t; argDeclExpr = arrowMethodCall(argExpr.clone(), "To" + t); argType = typeRef(v8Ident("Handle", v8Ident(t))); argUsageExpr = cast(arg.getValueType().clone(), arrowMethodCall(expr(UnaryOperator.Dereference, varRef(arg.getName())), "Value")); break; case VAList: throw new UnsupportedConversionException(function, "va_list not supported yet"); case Unknown: case Void: throw new UnsupportedConversionException(arg, "Cannot convert arguments of type " + tr); } if (typeTest != null) { body.addStatement( new If( expr(UnaryOperator.Not, typeTest), new Return( methodCall(v8Ident("ThrowException"), methodCall(ident("v8", "Exception", "TypeError"), newV8String("Invalid value for argument '" + arg.getName() + "' at index " + iArg + (typeErrorMessage == null ? "" : ": " + typeErrorMessage))))))); } if (argDeclExpr != null) { body.addStatement(stat(new VariablesDeclaration(argType, new Declarator.DirectDeclarator(arg.getName(), argDeclExpr)))); } if (argUsageExpr != null) { params.add(argUsageExpr); } } TypeRef retTr = resolveTypeDef(function.getValueType()); NodeType retNodeType = getNodeType(retTr); body.addStatement(new VariablesDeclaration(typeRef(v8Ident("HandleScope")), new Declarator.DirectDeclarator(scopeName))); Expression call = methodCall(function.getName(), params.toArray(new Expression[params.size()])); if (retNodeType == NodeType.Void) { body.addStatement(stat(call)); } else { VariablesDeclaration retDecl = new VariablesDeclaration(function.getValueType().clone(), new Declarator.DirectDeclarator(returnValueName, call)); body.addStatement(retDecl); } Expression retExpr; switch (retNodeType) { case Number: body.addStatement(new Return(scopeClose(methodCall(ident("v8", "Number", "New"), varRef(returnValueName))))); break; case Boolean: body.addStatement(new Return(scopeClose(methodCall(ident("v8", "Boolean", "New"), varRef(returnValueName))))); break; case Pointer: // TODO: add pointer validity info. body.addStatement( new If( varRef(returnValueName), new Return( scopeClose( memberRef( methodCall(ident("node", "Buffer", "New"), cast(new TypeRef.Pointer(typeRef("char"), Declarator.PointerStyle.Pointer), varRef(returnValueName)), expr(1), varRef(dummyNodeBufferFreeCallbackName), varRef("NULL")), Expression.MemberRefStyle.Arrow, "handle_"))), new Return(scopeClose(methodCall(ident("v8", "Null")))))); break; case Void: body.addStatement(new Return(scopeClose(methodCall(v8Ident("Undefined"))))); break; default: throw new UnsupportedConversionException(function, "Return type not handled: " + retTr + " (" + retNodeType + ")"); } method.setBody(body); implementations.addDeclaration(method); Block initBlock = getInitMethodBody(implementations); initBlock.addStatement(stat(methodCall("NODE_SET_METHOD", varRef(initTarget), expr(functionName.toString()), varRef(methodName)))); } private Expression scopeClose(Expression content) { return methodCall(varRef(scopeName), Expression.MemberRefStyle.Dot, "Close", content); } private TypeRef transformTypeForCast(TypeRef tr) { if (tr instanceof TypeRef.TargettedTypeRef) { TypeRef.TargettedTypeRef ttr = (TypeRef.TargettedTypeRef) tr; return new TypeRef.Pointer(transformTypeForCast(ttr.getTarget()), Declarator.PointerStyle.Pointer); } return tr.clone(); } @Override protected Struct createFakePointerClass(Identifier fakePointer) { return null; //throw new UnsupportedOperationException("Not supported yet."); } @Override public void convertEnum(com.ochafik.lang.jnaerator.parser.Enum e, Signatures signatures, DeclarationsHolder out, Identifier libraryClassName) { //throw new UnsupportedOperationException("Not supported yet."); } @Override public Struct convertStruct(Struct struct, Signatures signatures, Identifier callerLibraryClass, String callerLibrary, boolean onlyFields) throws IOException { return null; //throw new UnsupportedOperationException("Not supported yet."); } @Override public boolean convertVariablesDeclaration(VariablesDeclaration v, Signatures signatures, DeclarationsHolder out, int[] iChild, boolean isGlobal, Identifier holderName, Identifier callerLibraryClass, String callerLibrary) { throw new UnsupportedOperationException("Not supported yet."); } public static Identifier v8Ident(String name, Identifier... args) { Expression[] exprArgs = new Expression[args.length]; for (int i = 0; i < args.length; i++) { exprArgs[i] = expr(typeRef(args[i])); } return templateIdent(ident("v8", name), exprArgs); } @Override public void generateLibraryFiles(SourceFiles sourceFiles, Result result, JNAeratorConfig config) throws IOException { List<Object> gypTargets = new ArrayList(); for (String library : result.libraries) { if (library == null) { continue; // to handle code defined in macro-expanded expressions } boolean isFramework = config.frameworks.contains(library); String moduleName = library; String sourcePath = "src/" + library + "_module.cc"; final List<String> sources = new ArrayList<String>(), dependencies = new ArrayList<String>(); // TODO: list sources in fillLibraryMapping sources.add(sourcePath); //out.println("///\n/// This file was autogenerated by JNAerator (http://jnaerator.googlecode.com/), \n/// a tool written by Olivier Chafik (http://ochafik.com/).\n///"); SourceFile sourceFile = new SourceFile(); sourceFile.setElementFile(sourcePath); for (String inc : new String[]{"v8.h", "node.h", "node_buffer.h"}) { sourceFile.addDeclaration(new Include(Include.Type.CInclude, inc)); } if (!isFramework) { List<File> librarySourceFiles = config.sourceFilesByLibrary.get(library); if (librarySourceFiles != null) { for (File file : librarySourceFiles) { sourceFile.addDeclaration(new Include(Include.Type.CInclude, file.toString())); } } } for (String otherFramework : config.frameworks) { sourceFile.addDeclaration(new Include(Include.Type.ObjCImport, otherFramework + "/" + otherFramework + ".h")); } String initFunctionName = library + "_init"; Function initMethod = new Function(Function.Type.CppMethod, ident(initFunctionName), typeRef(void.class)); initMethod.addArg(new Arg(initTarget, typeRef(v8Ident("Handle", v8Ident("Object"))))); initMethod.setBody(new Block()); // Hack: add init method here so that convertFunction finds it, then (re)move it to the end. sourceFile.addDeclaration(initMethod); Function dummyNodeBufferFreeCallback = new Function( Function.Type.CFunction, ident(dummyNodeBufferFreeCallbackName), typeRef(void.class), new Arg("data", new TypeRef.Pointer(typeRef("char"), Declarator.PointerStyle.Pointer)), new Arg("hint", new TypeRef.Pointer(typeRef("void"), Declarator.PointerStyle.Pointer))); dummyNodeBufferFreeCallback.setBody(new Block()); sourceFile.addDeclaration(dummyNodeBufferFreeCallback); fillLibraryMapping(result, sourceFiles, sourceFile, sourceFile, library, null, null); initMethod.replaceBy(null); sourceFile.addDeclaration(initMethod); sourceFile.addDeclaration(decl(stat(methodCall("NODE_MODULE", varRef(ident(library)), varRef(ident(initFunctionName)))))); writeLibraryInterface(result, sourceFiles, sourceFile, library, null); Map<String, Object> gypTarget = map(); gypTarget.put("target_name", library); gypTarget.put("sources", sources); gypTarget.put("include_dirs", config.preprocessorConfig.explicitIncludes); gypTarget.put("dependencies", dependencies); Map<String, Object> linkSettings = map(); linkSettings.put("libraries", isFramework ? Arrays.asList("-framework", library) : Arrays.asList("-l" + library)); List<Object> defines = new ArrayList<Object>(); for (Map.Entry<String, String> e : config.preprocessorConfig.explicitMacros.entrySet()) { String k = e.getKey(), v = e.getValue(); defines.add(v == null || v.length() == 0 ? k : k + "=" + v); } gypTarget.put("defines", defines); gypTarget.put("link_settings", linkSettings); gypTargets.add(gypTarget); } Map<String, Object> gypContents = map(); gypContents.put("targets", gypTargets); final PrintWriter gypOut = result.classOutputter.getSourceWriter("binding.gyp"); gypOut.print(toGYP(gypContents)); gypOut.close(); } }