001/*******************************************************************************
002 * Copyright (C) 2009-2011 FuseSource Corp.
003 * Copyright (c) 2004, 2007 IBM Corporation and others.
004 *
005 * All rights reserved. This program and the accompanying materials
006 * are made available under the terms of the Eclipse Public License v1.0
007 * which accompanies this distribution, and is available at
008 * http://www.eclipse.org/legal/epl-v10.html
009 *
010 *******************************************************************************/
011package org.fusesource.hawtjni.generator;
012
013import java.lang.reflect.Modifier;
014import java.util.ArrayList;
015import java.util.List;
016
017import org.fusesource.hawtjni.generator.model.JNIClass;
018import org.fusesource.hawtjni.generator.model.JNIField;
019import org.fusesource.hawtjni.generator.model.JNIMethod;
020import org.fusesource.hawtjni.generator.model.JNIParameter;
021import org.fusesource.hawtjni.generator.model.JNIType;
022import org.fusesource.hawtjni.runtime.ArgFlag;
023import org.fusesource.hawtjni.runtime.ClassFlag;
024import org.fusesource.hawtjni.runtime.FieldFlag;
025import org.fusesource.hawtjni.runtime.MethodFlag;
026
027import static org.fusesource.hawtjni.runtime.MethodFlag.*;
028
029/**
030 * 
031 * @author <a href="http://hiramchirino.com">Hiram Chirino</a>
032 */
033public class NativesGenerator extends JNIGenerator {
034
035    boolean enterExitMacro;
036
037    public NativesGenerator() {
038        enterExitMacro = true;
039    }
040
041    public void generateCopyright() {
042        outputln(fixDelimiter(getCopyright()));
043    }
044
045    public void generateIncludes() {
046        String outputName = getOutputName();
047        outputln("#include \"" + outputName + ".h\"");
048        outputln("#include \"hawtjni.h\"");
049        outputln("#include \"" + outputName + "_structs.h\"");
050        outputln("#include \"" + outputName + "_stats.h\"");
051        outputln();
052    }
053
054    public void generate(JNIClass clazz) {
055        List<JNIMethod> methods = clazz.getNativeMethods();
056        if( methods.isEmpty() ) {
057            return;
058        }
059        sortMethods(methods);
060        generateNativeMacro(clazz);
061        generate(methods);
062    }
063
064    public void generate(List<JNIMethod> methods) {
065        sortMethods(methods);
066        for (JNIMethod method : methods) {
067            if ((method.getModifiers() & Modifier.NATIVE) == 0)
068                continue;
069            generate(method);
070            if (progress != null)
071                progress.step();
072        }
073    }
074
075    boolean isStruct(ArgFlag flags[]) {
076        for (ArgFlag flag : flags) {
077            if (flag.equals(ArgFlag.BY_VALUE))
078                return true;
079        }
080        return false;
081    }
082
083    void generateCallback(JNIMethod method, String function, List<JNIParameter> params, JNIType returnType) {
084        output("static jintLong ");
085        output(function);
086        outputln(";");
087        output("static ");
088        String[] types = method.getCallbackTypes();
089        ArgFlag[][] flags = method.getCallbackFlags();
090        output(types[0]);
091        output(" ");
092        output("proc_");
093        output(function);
094        output("(");
095        boolean first = true;
096        for (int i = 1; i < types.length; i++) {
097            if (!first)
098                output(", ");
099            output(types[i]);
100            output(" ");
101            output("arg");
102            output(String.valueOf(i - 1));
103            first = false;
104        }
105        outputln(") {");
106
107        output("\t");
108        if (isStruct(flags[0])) {
109            output(types[0]);
110            output("* lprc = ");
111        } else if (!types[0].equals("void")) {
112            output("return ");
113        }
114        output("((");
115        output(types[0]);
116        if (isStruct(flags[0]))
117            output("*");
118        output(" (*)(");
119        first = true;
120        for (int i = 1; i < types.length; i++) {
121            if (!first)
122                output(", ");
123            first = false;
124            output(types[i]);
125            if (isStruct(flags[i]))
126                output("*");
127        }
128        output("))");
129        output(function);
130        output(")(");
131        first = true;
132        for (int i = 1; i < types.length; i++) {
133            if (!first)
134                output(", ");
135            first = false;
136            if (isStruct(flags[i]))
137                output("&");
138            output("arg");
139            output(String.valueOf(i - 1));
140        }
141        outputln(");");
142        if (isStruct(flags[0])) {
143            output("\t");
144            output(types[0]);
145            outputln(" rc;");
146            outputln("\tif (lprc) {");
147            outputln("\t\trc = *lprc;");
148            outputln("\t\tfree(lprc);");
149            outputln("\t} else {");
150            output("\t\tmemset(&rc, 0, sizeof(");
151            output(types[0]);
152            outputln("));");
153            outputln("\t}");
154            outputln("\treturn rc;");
155        }
156        outputln("}");
157
158        output("static jintLong ");
159        output(method.getName());
160        outputln("(jintLong func) {");
161        output("\t");
162        output(function);
163        outputln(" = func;");
164        output("\treturn (jintLong)proc_");
165        output(function);
166        outputln(";");
167        outputln("}");
168    }
169    
170    private void generateConstantsInitializer(JNIMethod method) {
171        JNIClass clazz = method.getDeclaringClass();
172        ArrayList<JNIField> constants = getConstantFields(clazz);
173        if( constants.isEmpty() ) {
174            return;
175        }
176        
177        if (isCPP) {
178            output("extern \"C\" ");
179        }
180        outputln("JNIEXPORT void JNICALL "+clazz.getSimpleName()+"_NATIVE("+toC(method.getName())+")(JNIEnv *env, jclass that)");
181        outputln("{");
182        for (JNIField field : constants) {
183
184            String conditional = field.getConditional();
185            if (conditional!=null) {
186                outputln("#if "+conditional);
187            }
188            JNIType type = field.getType(), type64 = field.getType64();
189            boolean allowConversion = !type.equals(type64);
190            
191            String simpleName = type.getSimpleName();
192            String accessor = field.getAccessor();
193            if (accessor == null || accessor.length() == 0)
194                accessor = field.getName();
195
196            String fieldId = "(*env)->GetStaticFieldID(env, that, \""+field.getName()+"\", \""+type.getTypeSignature(allowConversion)+"\")";
197            if (isCPP) {
198                fieldId = "env->GetStaticFieldID(that, \""+field.getName()+"\", \""+type.getTypeSignature(allowConversion)+"\")";
199            }
200
201            if (type.isPrimitive()) {
202                if (isCPP) {
203                    output("\tenv->SetStatic"+type.getTypeSignature1(allowConversion)+"Field(that, "+fieldId +", ");
204                } else {
205                    output("\t(*env)->SetStatic"+type.getTypeSignature1(allowConversion)+"Field(env, that, "+fieldId +", ");
206                }
207                output("("+type.getTypeSignature2(allowConversion)+")");
208                if( field.isPointer() ) {
209                    output("(intptr_t)");
210                }
211                output(accessor);
212                output(");");
213                
214            } else if (type.isArray()) {
215                JNIType componentType = type.getComponentType(), componentType64 = type64.getComponentType();
216                if (componentType.isPrimitive()) {
217                    outputln("\t{");
218                    output("\t");
219                    output(type.getTypeSignature2(allowConversion));
220                    output(" lpObject1 = (");
221                    output(type.getTypeSignature2(allowConversion));
222                    if (isCPP) {
223                        output(")env->GetStaticObjectField(that, ");
224                    } else {
225                        output(")(*env)->GetStaticObjectField(env, that, ");
226                    }
227                    output(field.getDeclaringClass().getSimpleName());
228                    output(fieldId);
229                    outputln(");");
230                    if (isCPP) {
231                        output("\tenv->Set");
232                    } else {
233                        output("\t(*env)->Set");
234                    }
235                    output(componentType.getTypeSignature1(!componentType.equals(componentType64)));
236                    if (isCPP) {
237                        output("ArrayRegion(lpObject1, 0, sizeof(");
238                    } else {
239                        output("ArrayRegion(env, lpObject1, 0, sizeof(");
240                    }
241                    output(accessor);
242                    output(")");
243                    if (!componentType.isType("byte")) {
244                        output(" / sizeof(");
245                        output(componentType.getTypeSignature2(!componentType.equals(componentType64)));
246                        output(")");
247                    }
248                    output(", (");
249                    output(type.getTypeSignature4(allowConversion, false));
250                    output(")");
251                    output(accessor);
252                    outputln(");");
253                    output("\t}");
254                } else {
255                    throw new Error("not done");
256                }
257            } else {
258                outputln("\t{");
259                if (isCPP) {
260                    output("\tjobject lpObject1 = env->GetStaticObjectField(that, ");
261                } else {
262                    output("\tjobject lpObject1 = (*env)->GetStaticObjectField(env, that, ");
263                }
264                output(field.getDeclaringClass().getSimpleName());
265                output("Fc.");
266                output(field.getName());
267                outputln(");");
268                output("\tif (lpObject1 != NULL) set");
269                output(simpleName);
270                output("Fields(env, lpObject1, &lpStruct->");
271                output(accessor);
272                outputln(");");
273                output("\t}");
274            }
275            outputln();
276            if (conditional!=null) {
277                outputln("#endif");
278            }
279        }
280        outputln("   return;");
281        outputln("}");
282
283    }
284    
285    private ArrayList<JNIField> getConstantFields(JNIClass clazz) {
286        ArrayList<JNIField> rc = new ArrayList<JNIField>();
287        List<JNIField> fields = clazz.getDeclaredFields();
288        for (JNIField field : fields) {
289            int mods = field.getModifiers();
290            if ( (mods & Modifier.STATIC) != 0 && field.getFlag(FieldFlag.CONSTANT)) {
291                rc.add(field);
292            }
293        }
294        return rc;
295    }
296    
297    public void generate(JNIMethod method) {
298        if (method.getFlag(MethodFlag.METHOD_SKIP))
299            return;
300        
301        JNIType returnType = method.getReturnType32(), returnType64 = method.getReturnType64();
302
303        if( method.getFlag(CONSTANT_INITIALIZER)) {
304            if( returnType.isType("void") && method.getParameters().isEmpty() ) {
305                generateConstantsInitializer(method);
306            } else {
307                output("#error Warning: invalid CONSTANT_INITIALIZER tagged method. It must be void and take no arguments: ");
308                outputln(method.toString());
309            }
310            return;
311        }
312        
313        if (!(returnType.isType("void") || returnType.isPrimitive() || isSystemClass(returnType) || returnType.isType("java.lang.String"))) {
314            output("#error Warning: bad return type. :");
315            outputln(method.toString());
316            return;
317        }
318        
319        String conditional = method.getConditional();
320        if (conditional!=null) {
321            outputln("#if "+conditional);
322        }
323        
324        List<JNIParameter> params = method.getParameters();
325        String function = getFunctionName(method), function64 = getFunctionName(method, method.getParameterTypes64());
326        boolean sameFunction = function.equals(function64);
327        if (!sameFunction) {
328            output("#ifndef ");
329            output(JNI64);
330            outputln();
331        }
332        if (isCPP) {
333            output("extern \"C\" ");
334            generateFunctionPrototype(method, function, params, returnType, returnType64, true);
335            outputln(";");
336        }
337        if (function.startsWith("CALLBACK_")) {
338            generateCallback(method, function, params, returnType);
339        }
340        generateFunctionPrototype(method, function, params, returnType, returnType64, !sameFunction);
341        if (!function.equals(function64)) {
342            outputln();
343            outputln("#else");
344            if (isCPP) {
345                output("extern \"C\" ");
346                generateFunctionPrototype(method, function64, params, returnType, returnType64, true);
347                outputln(";");
348            }
349            generateFunctionPrototype(method, function64, params, returnType, returnType64, !sameFunction);
350            outputln();
351            outputln("#endif");
352        }
353        generateFunctionBody(method, function, function64, params, returnType, returnType64);
354        if (conditional!=null) {
355            outputln("#endif");
356        }
357        outputln();
358    }
359
360    public void setEnterExitMacro(boolean enterExitMacro) {
361        this.enterExitMacro = enterExitMacro;
362    }
363
364    void generateNativeMacro(JNIClass clazz) {
365        output("#define ");
366        output(clazz.getSimpleName());
367        output("_NATIVE(func) Java_");
368        output(toC(clazz.getName()));
369        outputln("_##func");
370        outputln();
371    }
372
373    boolean generateGetParameter(JNIMethod method, JNIParameter param, boolean critical, int indent) {
374        JNIType paramType = param.getType32(), paramType64 = param.getType64();
375        if (paramType.isPrimitive() || isSystemClass(paramType))
376            return false;
377        String iStr = String.valueOf(param.getParameter());
378        for (int j = 0; j < indent; j++)
379            output("\t");
380        output("if (arg");
381        output(iStr);
382        output(") if ((lparg");
383        output(iStr);
384        output(" = ");
385        if (paramType.isArray()) {
386            JNIType componentType = paramType.getComponentType();
387            if (componentType.isPrimitive()) {
388                if( "long".equals( componentType.getName() ) && param.isPointer() ) {
389                    // This case is special as we may need to do pointer conversions..
390                    // if your on a 32 bit system but are keeping track of the pointers in a 64 bit long
391                    output("hawtjni_malloc_pointer_array(env, arg");
392                    output(iStr);
393                    output(")");
394                } else if (critical) {
395                    if (isCPP) {
396                        output("(");
397                        output(componentType.getTypeSignature2(!paramType.equals(paramType64)));
398                        output("*)");
399                        output("env->GetPrimitiveArrayCritical(arg");
400                    } else {
401                        output("(*env)->GetPrimitiveArrayCritical(env, arg");
402                    }
403                    output(iStr);
404                    output(", NULL)");
405                } else {
406                    if (isCPP) {
407                        output("env->Get");
408                    } else {
409                        output("(*env)->Get");
410                    }
411                    output(componentType.getTypeSignature1(!paramType.equals(paramType64)));
412                    if (isCPP) {
413                        output("ArrayElements(arg");
414                    } else {
415                        output("ArrayElements(env, arg");
416                    }
417                    output(iStr);
418                    output(", NULL)");
419                }
420            } else {
421                throw new Error("not done");
422            }
423        } else if (paramType.isType("java.lang.String")) {
424            if (param.getFlag(ArgFlag.UNICODE)) {
425                if (isCPP) {
426                    output("env->GetStringChars(arg");
427                } else {
428                    output("(*env)->GetStringChars(env, arg");
429                }
430                output(iStr);
431                output(", NULL)");
432            } else {
433                if (isCPP) {
434                    output("env->GetStringUTFChars(arg");
435                } else {
436                    output("(*env)->GetStringUTFChars(env, arg");
437                }
438                output(iStr);
439                output(", NULL)");
440            }
441        } else {
442            if (param.getFlag(ArgFlag.NO_IN)) {
443                output("&_arg");
444                output(iStr);
445            } else {
446                output("get");
447                output(paramType.getSimpleName());
448                output("Fields(env, arg");
449                output(iStr);
450                output(", &_arg");
451                output(iStr);
452                output(")");
453            }
454        }
455        outputln(") == NULL) goto fail;");
456        return true;
457    }
458
459    void generateSetParameter(JNIParameter param, boolean critical) {
460        JNIType paramType = param.getType32(), paramType64 = param.getType64();
461        if (paramType.isPrimitive() || isSystemClass(paramType))
462            return;
463        String iStr = String.valueOf(param.getParameter());
464        if (paramType.isArray()) {
465            output("\tif (arg");
466            output(iStr);
467            output(" && lparg");
468            output(iStr);
469            output(") ");
470            JNIType componentType = paramType.getComponentType();
471            if (componentType.isPrimitive()) {
472                if( "long".equals( componentType.getName() ) && param.isPointer() ) {
473                    // This case is special as we may need to do pointer conversions..
474                    // if your on a 32 bit system but are keeping track of the pointers in a 64 bit long
475                    output("hawtjni_free_pointer_array(env, arg");
476                    output(iStr);
477                } else if (critical) {
478                    if (isCPP) {
479                        output("env->ReleasePrimitiveArrayCritical(arg");
480                    } else {
481                        output("(*env)->ReleasePrimitiveArrayCritical(env, arg");
482                    }
483                    output(iStr);
484                } else {
485                    if (isCPP) {
486                        output("env->Release");
487                    } else {
488                        output("(*env)->Release");
489                    }
490                    output(componentType.getTypeSignature1(!paramType.equals(paramType64)));
491                    if (isCPP) {
492                        output("ArrayElements(arg");
493                    } else {
494                        output("ArrayElements(env, arg");
495                    }
496                    output(iStr);
497                }
498                output(", lparg");
499                output(iStr);
500                output(", ");
501                if (param.getFlag(ArgFlag.NO_OUT)) {
502                    output("JNI_ABORT");
503                } else {
504                    output("0");
505                }
506                output(");");
507            } else {
508                throw new Error("not done");
509            }
510            outputln();
511        } else if (paramType.isType("java.lang.String")) {
512            output("\tif (arg");
513            output(iStr);
514            output(" && lparg");
515            output(iStr);
516            output(") ");
517            if (param.getFlag(ArgFlag.UNICODE)) {
518                if (isCPP) {
519                    output("env->ReleaseStringChars(arg");
520                } else {
521                    output("(*env)->ReleaseStringChars(env, arg");
522                }
523            } else {
524                if (isCPP) {
525                    output("env->ReleaseStringUTFChars(arg");
526                } else {
527                    output("(*env)->ReleaseStringUTFChars(env, arg");
528                }
529            }
530            output(iStr);
531            output(", lparg");
532            output(iStr);
533            outputln(");");
534        } else {
535            if (!param.getFlag(ArgFlag.NO_OUT)) {
536                output("\tif (arg");
537                output(iStr);
538                output(" && lparg");
539                output(iStr);
540                output(") ");
541                output("set");
542                output(paramType.getSimpleName());
543                output("Fields(env, arg");
544                output(iStr);
545                output(", lparg");
546                output(iStr);
547                outputln(");");
548            }
549        }
550    }
551
552    void generateEnterExitMacro(JNIMethod method, String function, String function64, boolean enter) {
553        if (!enterExitMacro)
554            return;
555        if (!function.equals(function64)) {
556            output("#ifndef ");
557            output(JNI64);
558            outputln();
559        }
560        output("\t");
561        output(method.getDeclaringClass().getSimpleName());
562        output("_NATIVE_");
563        output(enter ? "ENTER" : "EXIT");
564        output("(env, that, ");
565        output(method.getDeclaringClass().getSimpleName()+"_"+function);
566        outputln("_FUNC);");
567        if (!function.equals(function64)) {
568            outputln("#else");
569            output("\t");
570            output(method.getDeclaringClass().getSimpleName());
571            output("_NATIVE_");
572            output(enter ? "ENTER" : "EXIT");
573            output("(env, that, ");
574            output(method.getDeclaringClass().getSimpleName()+"_"+function64);
575            outputln("_FUNC);");
576            outputln("#endif");
577        }
578    }
579
580    boolean generateLocalVars(JNIMethod method, List<JNIParameter> params, JNIType returnType, JNIType returnType64) {
581        boolean needsReturn = enterExitMacro;
582        for (int i = 0; i < params.size(); i++) {
583            JNIParameter param = params.get(i);
584            JNIType paramType = param.getType32(), paramType64 = param.getType64();
585            if (paramType.isPrimitive() || isSystemClass(paramType))
586                continue;
587            output("\t");
588            if (paramType.isArray()) {
589                JNIType componentType = paramType.getComponentType();
590                if( "long".equals( componentType.getName() ) && param.isPointer() ) {
591                    output("void **lparg" + i+"=NULL;");
592                } else if (componentType.isPrimitive()) {
593                    output(componentType.getTypeSignature2(!paramType.equals(paramType64)));
594                    output(" *lparg" + i);
595                    output("=NULL;");
596                } else {
597                    throw new Error("not done");
598                }
599            } else if (paramType.isType("org.fusesource.hawtjni.runtime.JNIEnv")) {
600                // no need to generate a local for this one..
601            } else if (paramType.isType("java.lang.String")) {
602                if (param.getFlag(ArgFlag.UNICODE)) {
603                    output("const jchar *lparg" + i);
604                } else {
605                    output("const char *lparg" + i);
606                }
607                output("= NULL;");
608            } else {
609                if (param.getTypeClass().getFlag(ClassFlag.STRUCT) && !param.getTypeClass().getFlag(ClassFlag.TYPEDEF)) {
610                    output("struct ");
611                }
612                output(paramType.getNativeName());
613                output(" _arg" + i);
614                if (param.getFlag(ArgFlag.INIT))
615                    output("={0}");
616                output(", *lparg" + i);
617                output("=NULL;");
618            }
619            outputln();
620            needsReturn = true;
621        }
622        if (needsReturn) {
623            if (!returnType.isType("void")) {
624                output("\t");
625                output(returnType.getTypeSignature2(!returnType.equals(returnType64)));
626                outputln(" rc = 0;");
627            }
628        }
629        return needsReturn;
630    }
631
632    boolean generateGetters(JNIMethod method, List<JNIParameter> params) {
633        boolean genFailTag = false;
634        int criticalCount = 0;
635        for (JNIParameter param : params) {
636            if( !"org.fusesource.hawtjni.runtime.JNIEnv".equals(param.getTypeClass().getName()) ) {
637                if (!isCritical(param)) {
638                    genFailTag |= generateGetParameter(method, param, false, 1);
639                } else {
640                    criticalCount++;
641                }
642            }
643        }
644        if (criticalCount != 0) {
645            outputln("#ifdef JNI_VERSION_1_2");
646            outputln("\tif (IS_JNI_1_2) {");
647            for (JNIParameter param : params) {
648                if( !"org.fusesource.hawtjni.runtime.JNIEnv".equals(param.getTypeClass().getName()) ) {
649                    if (isCritical(param)) {
650                        genFailTag |= generateGetParameter(method, param, true, 2);
651                    }
652                }
653            }
654            outputln("\t} else");
655            outputln("#endif");
656            outputln("\t{");
657            for (JNIParameter param : params) {
658                if( !"org.fusesource.hawtjni.runtime.JNIEnv".equals(param.getTypeClass().getName()) ) {
659                    if (isCritical(param)) {
660                        genFailTag |= generateGetParameter(method, param, false, 2);
661                    }
662                }
663            }
664            outputln("\t}");
665        }
666        return genFailTag;
667    }
668
669    void generateSetters(JNIMethod method, List<JNIParameter> params) {
670        int criticalCount = 0;
671        for (int i = params.size() - 1; i >= 0; i--) {
672            JNIParameter param = params.get(i);
673            if( !"org.fusesource.hawtjni.runtime.JNIEnv".equals(param.getTypeClass().getName()) ) {
674                if (isCritical(param)) {
675                    criticalCount++;
676                }
677            }
678        }
679        if (criticalCount != 0) {
680            outputln("#ifdef JNI_VERSION_1_2");
681            outputln("\tif (IS_JNI_1_2) {");
682            for (int i = params.size() - 1; i >= 0; i--) {
683                JNIParameter param = params.get(i);
684                if( !"org.fusesource.hawtjni.runtime.JNIEnv".equals(param.getTypeClass().getName()) ) {
685                    if (isCritical(param)) {
686                        output("\t");
687                        generateSetParameter(param, true);
688                    }
689                }
690            }
691            outputln("\t} else");
692            outputln("#endif");
693            outputln("\t{");
694            for (int i = params.size() - 1; i >= 0; i--) {
695                JNIParameter param = params.get(i);
696                if( !"org.fusesource.hawtjni.runtime.JNIEnv".equals(param.getTypeClass().getName()) ) {
697                    if (isCritical(param)) {
698                        output("\t");
699                        generateSetParameter(param, false);
700                    }
701                }
702            }
703            outputln("\t}");
704        }
705        for (int i = params.size() - 1; i >= 0; i--) {
706            JNIParameter param = params.get(i);
707            if( !"org.fusesource.hawtjni.runtime.JNIEnv".equals(param.getTypeClass().getName()) ) {
708                if (!isCritical(param)) {
709                    generateSetParameter(param, false);
710                }
711            }
712        }
713    }
714
715    void generateDynamicFunctionCall(JNIMethod method, List<JNIParameter> params, JNIType returnType, JNIType returnType64, boolean needsReturn) {
716        outputln("/*");
717        generateFunctionCall(method, params, returnType, returnType64, needsReturn);
718        outputln("*/");
719        outputln("\t{");
720
721        String name = method.getName();
722        if (name.startsWith("_"))
723            name = name.substring(1);
724        output("\t\tLOAD_FUNCTION(fp, ");
725        output(name);
726        outputln(")");
727        outputln("\t\tif (fp) {");
728        output("\t\t");
729        generateFunctionCallLeftSide(method, returnType, returnType64, needsReturn);
730        output("((");
731        output(returnType.getTypeSignature2(!returnType.equals(returnType64)));
732        output(" (CALLING_CONVENTION*)(");
733        for (int i = 0; i < params.size(); i++) {
734            if (i != 0)
735                output(", ");
736            JNIParameter param = params.get(i);
737            String cast = param.getCast();
738            if( param.isPointer() ) {
739                output("(intptr_t)");
740            }
741            boolean isStruct = param.getFlag(ArgFlag.BY_VALUE);
742            if (cast.length() > 2) {
743                cast = cast.substring(1, cast.length() - 1);
744                if (isStruct) {
745                    int index = cast.lastIndexOf('*');
746                    if (index != -1)
747                        cast = cast.substring(0, index).trim();
748                }
749                output(cast);
750            } else {
751                JNIType paramType = param.getType32(), paramType64 = param.getType64();
752                output(paramType.getTypeSignature4(!paramType.equals(paramType64), isStruct));
753            }
754        }
755        output("))");
756        output("fp");
757        output(")");
758        generateFunctionCallRightSide(method, params, 0);
759        output(";");
760        outputln();
761        outputln("\t\t}");
762        outputln("\t}");
763    }
764
765    void generateFunctionCallLeftSide(JNIMethod method, JNIType returnType, JNIType returnType64, boolean needsReturn) {
766        output("\t");
767        if (!returnType.isType("void")) {
768            if (needsReturn) {
769                output("rc = ");
770            } else {
771                output("return ");
772            }
773
774            String cast = method.getCast();
775            if (cast.length() != 0 && !cast.equals("()")) {
776                if( method.isPointer() ) {
777                    output("(intptr_t)");
778                }
779                output(cast);
780            } else {
781                if( method.getFlag(CPP_NEW)) {
782                    String[] parts = getNativeNameParts(method);
783                    String className = parts[0];
784                    output("(intptr_t)("+className+" *)");
785                } else {
786                    output("(");
787                    output(returnType.getTypeSignature2(!returnType.equals(returnType64)));
788                    output(")");
789                }
790            }
791        }
792        if (method.getFlag(MethodFlag.ADDRESS)) {
793            output("&");
794        }
795        if (method.getFlag(MethodFlag.JNI)) {
796            output(isCPP ? "env->" : "(*env)->");
797        }
798    }
799
800    void generateFunctionCallRightSide(JNIMethod method, List<JNIParameter> params, int paramStart) {
801        if (!method.getFlag(MethodFlag.CONSTANT_GETTER)) {
802            output("(");
803            if (method.getFlag(MethodFlag.JNI)) {
804                if (!isCPP)
805                    output("env, ");
806            }
807            for (int i = paramStart; i < params.size(); i++) {
808                JNIParameter param = params.get(i);
809                if (i != paramStart)
810                    output(", ");
811                if (param.getFlag(ArgFlag.BY_VALUE))
812                    output("*");
813                output(param.getCast());
814                if( param.isPointer() ) {
815                    output("(intptr_t)");
816                }
817                if (param.getFlag(ArgFlag.CS_OBJECT))
818                    output("TO_OBJECT(");
819                if (i == params.size() - 1 && param.getFlag(ArgFlag.SENTINEL)) {
820                    output("NULL");
821                } else {
822                    if( "org.fusesource.hawtjni.runtime.JNIEnv".equals(param.getTypeClass().getName()) ) {
823                        output("env");
824                    } else {
825                        JNIType paramType = param.getType32();
826                        if (!paramType.isPrimitive() && !isSystemClass(paramType))
827                            output("lp");
828                        output("arg" + i);
829                    }
830                }
831                if (param.getFlag(ArgFlag.CS_OBJECT))
832                    output(")");
833            }
834            output(")");
835        }
836    }
837
838    static String[] getNativeNameParts(JNIMethod method) {
839        String className = null;
840        String methodName = null;
841
842        JNIClass dc = method.getDeclaringClass();
843        if( dc.getFlag(ClassFlag.CPP) || dc.getFlag(ClassFlag.STRUCT) ) {
844            className = method.getDeclaringClass().getNativeName();
845        }
846
847        if( method.getAccessor().length() != 0 ) {
848            methodName = method.getAccessor();
849            int pos = methodName.lastIndexOf("::");
850            if( pos >= 0 ) {
851                className = methodName.substring(0, pos);
852                methodName = methodName.substring(pos+2);
853            }
854        } else {
855            methodName = method.getName();
856            if( className==null ) {
857                int pos = methodName.indexOf("_");
858                if( pos > 0 ) {
859                    className = methodName.substring(0, pos);
860                    methodName = methodName.substring(pos+1);
861                }
862            }
863        }
864        if( className==null ) {
865            throw new Error(String.format("Could not determine object type name of method '%s'", method.getDeclaringClass().getSimpleName()+"."+method.getName()));
866        }
867        return new String[]{className, methodName};
868    }
869
870    void generateFunctionCall(JNIMethod method, List<JNIParameter> params, JNIType returnType, JNIType returnType64, boolean needsReturn) {
871        String name = method.getName();
872        String copy = method.getCopy();
873        boolean makeCopy = copy.length() != 0 && isCPP && !returnType.isType("void");
874        if (makeCopy) {
875            output("\t{");
876            output("\t\t");
877            output(copy);
878            output(" temp = ");
879        } else {
880            generateFunctionCallLeftSide(method, returnType, returnType64, needsReturn);
881        }
882        int paramStart = 0;
883        if (name.startsWith("_"))
884            name = name.substring(1);
885
886        boolean objc_struct = false;
887        if (name.equals("objc_msgSend_stret") || name.equals("objc_msgSendSuper_stret"))
888            objc_struct = true;
889        if (objc_struct) {
890            outputln("if (sizeof(_arg0) > STRUCT_SIZE_LIMIT) {");
891            generate_objc_msgSend_stret(method, params, name);
892            paramStart = 1;
893        } else if (name.equalsIgnoreCase("call")) {
894            output("(");
895            JNIParameter param = params.get(0);
896            String cast = param.getCast();
897            if (cast.length() != 0 && !cast.equals("()")) {
898                output(cast);
899                if( param.isPointer() ) {
900                    output("(intptr_t)");
901                }
902            } else {
903                output("(");
904                output(returnType.getTypeSignature2(!returnType.equals(returnType64)));
905                output(" (*)())");
906            }
907            output("arg0)");
908            paramStart = 1;
909        } else if (name.startsWith("VtblCall") || name.startsWith("_VtblCall")) {
910            output("((");
911            output(returnType.getTypeSignature2(!returnType.equals(returnType64)));
912            output(" (STDMETHODCALLTYPE *)(");
913            for (int i = 1; i < params.size(); i++) {
914                if (i != 1)
915                    output(", ");
916                JNIParameter param = params.get(i);
917                JNIType paramType = param.getType32(), paramType64 = param.getType64();
918                output(paramType.getTypeSignature4(!paramType.equals(paramType64), false));
919            }
920            output("))(*(");
921            JNIType paramType = params.get(1).getType32(), paramType64 = params.get(1).getType64();
922            output(paramType.getTypeSignature4(!paramType.equals(paramType64), false));
923            output(" **)arg1)[arg0])");
924            paramStart = 1;
925        } else if (method.getFlag(MethodFlag.CPP_METHOD) || method.getFlag(MethodFlag.SETTER) || method.getFlag(MethodFlag.GETTER) || method.getFlag(MethodFlag.ADDER)) {
926
927            String[] parts = getNativeNameParts(method);
928            String className = parts[0];
929            String methodName = parts[1];
930
931            if (method.getFlag(MethodFlag.CS_OBJECT)) {
932                output("TO_HANDLE(");
933            }
934            output("(");
935            if( params.isEmpty() ) {
936                throw new Error(String.format("C++ bound method '%s' missing the 'this' parameter", method.getDeclaringClass().getSimpleName()+"."+method.getName()));
937            }
938            JNIParameter param = params.get(0);
939            if (param.getFlag(ArgFlag.BY_VALUE))
940                output("*");
941            String cast = param.getCast();
942            if (cast.length() != 0 && !cast.equals("()")) {
943                output(cast);
944                if( param.isPointer() ) {
945                    output("(intptr_t)");
946                }
947            } else {
948                output("("+className+" *)(intptr_t)");
949            }
950            if (param.getFlag(ArgFlag.CS_OBJECT)) {
951                output("TO_OBJECT(");
952            }
953            output("arg0");
954            if (param.getFlag(ArgFlag.CS_OBJECT)) {
955                output(")");
956            }
957            output(")->");
958            output(methodName);
959            paramStart = 1;
960        } else if (method.getFlag(MethodFlag.CS_NEW)) {
961            output("TO_HANDLE(gcnew ");
962            String accessor = method.getAccessor();
963            if (accessor.length() != 0) {
964                output(accessor);
965            } else {
966                JNIClass dc = method.getDeclaringClass();
967                if( dc.getFlag(ClassFlag.CPP) || dc.getFlag(ClassFlag.STRUCT) ) {
968                    output(dc.getNativeName());
969                } else {
970                    int index = -1;
971                    if ((index = name.indexOf('_')) != -1) {
972                        output(name.substring(index + 1));
973                    } else {
974                        output(name);
975                    }
976                }
977            }
978        } else if (method.getFlag(MethodFlag.CPP_NEW)) {
979            if (method.getFlag(MethodFlag.CS_OBJECT)) {
980                output("TO_HANDLE(");
981            }
982            output("new ");
983            String accessor = method.getAccessor();
984            if (accessor.length() != 0) {
985                output(accessor);
986            } else {
987
988                JNIClass dc = method.getDeclaringClass();
989                if( dc.getFlag(ClassFlag.CPP) ) {
990                    output(method.getDeclaringClass().getNativeName());
991                } else {
992                    int index = -1;
993                    if ((index = name.indexOf('_')) != -1) {
994                        output(name.substring(index+1));
995                    } else {
996                        output(name);
997                    }
998                }
999
1000            }
1001        } else if (method.getFlag(MethodFlag.CPP_DELETE)) {
1002            String[] parts = getNativeNameParts(method);
1003            String className = parts[0];
1004
1005            output("delete ");
1006            JNIParameter param = params.get(0);
1007            String cast = param.getCast();
1008            if (cast.length() != 0 && !cast.equals("()")) {
1009                output(cast);
1010                if( param.isPointer() ) {
1011                    output("(intptr_t)");
1012                }
1013            } else {
1014                output("("+className+" *)(intptr_t)");
1015            }
1016            outputln("arg0;");
1017            return;
1018        } else {
1019            if (method.getFlag(MethodFlag.CS_OBJECT)) {
1020                output("TO_HANDLE(");
1021            }
1022            if (method.getFlag(MethodFlag.CAST)) {
1023                output("((");
1024                String returnCast = returnType.getTypeSignature2(!returnType.equals(returnType64));
1025                if (name.equals("objc_msgSend_bool") && returnCast.equals("jboolean")) {
1026                    returnCast = "BOOL";
1027                }
1028                output(returnCast);
1029                output(" (*)(");
1030                for (int i = 0; i < params.size(); i++) {
1031                    if (i != 0)
1032                        output(", ");
1033                    JNIParameter param = params.get(i);
1034                    String cast = param.getCast();
1035                    if (cast.length() != 0 && !cast.equals("()") ) {
1036                        if (cast.startsWith("("))
1037                            cast = cast.substring(1);
1038                        if (cast.endsWith(")"))
1039                            cast = cast.substring(0, cast.length() - 1);
1040                        output(cast);
1041                    } else {
1042                        JNIType paramType = param.getType32(), paramType64 = param.getType64();
1043                        if (!(paramType.isPrimitive() || paramType.isArray())) {
1044                            if (param.getTypeClass().getFlag(ClassFlag.STRUCT) && !param.getTypeClass().getFlag(ClassFlag.TYPEDEF)) {
1045                                output("struct ");
1046                            }
1047                        }
1048                        output(paramType.getTypeSignature4(!paramType.equals(paramType64), param.getFlag(ArgFlag.BY_VALUE)));
1049                    }
1050                }
1051                output("))");
1052            }
1053            String accessor = method.getAccessor();
1054            if (accessor.length() != 0) {
1055                output(accessor);
1056            } else {
1057                output(name);
1058            }
1059            if (method.getFlag(MethodFlag.CAST)) {
1060                output(")");
1061            }
1062        }
1063        if ((method.getFlag(MethodFlag.SETTER) && params.size() == 3) || (method.getFlag(MethodFlag.GETTER) && params.size() == 2)) {
1064            output("[arg1]");
1065            paramStart++;
1066        }
1067        if (method.getFlag(MethodFlag.SETTER))
1068            output(" = ");
1069        if (method.getFlag(MethodFlag.ADDER))
1070            output(" += ");
1071        if (!method.getFlag(MethodFlag.GETTER)) {
1072            generateFunctionCallRightSide(method, params, paramStart);
1073        }
1074        if (method.getFlag(MethodFlag.CS_NEW) || method.getFlag(MethodFlag.CS_OBJECT)) {
1075            output(")");
1076        }
1077        output(";");
1078        outputln();
1079        if (makeCopy) {
1080            outputln("\t\t{");
1081            output("\t\t\t");
1082            output(copy);
1083            output("* copy = new ");
1084            output(copy);
1085            outputln("();");
1086            outputln("\t\t\t*copy = temp;");
1087            output("\t\t\trc = ");
1088            output("(");
1089            output(returnType.getTypeSignature2(!returnType.equals(returnType64)));
1090            output(")");
1091            outputln("copy;");
1092            outputln("\t\t}");
1093            outputln("\t}");
1094        }
1095        if (objc_struct) {
1096            outputln("\t} else {");
1097            generate_objc_msgSend_stret(method, params, name.substring(0, name.length() - "_stret".length()));
1098            generateFunctionCallRightSide(method, params, 1);
1099            outputln(";");
1100            outputln("\t}");
1101        }
1102    }
1103
1104    void generate_objc_msgSend_stret(JNIMethod method, List<JNIParameter> params, String func) {
1105        output("\t\t*lparg0 = (*(");
1106        JNIType paramType = params.get(0).getType32(), paramType64 = params.get(0).getType64();
1107        output(paramType.getTypeSignature4(!paramType.equals(paramType64), true));
1108        output(" (*)(");
1109        for (int i = 1; i < params.size(); i++) {
1110            if (i != 1)
1111                output(", ");
1112            JNIParameter param = params.get(i);
1113            String cast = param.getCast();
1114            if( param.isPointer() ) {
1115                output("(intptr_t)");
1116            }
1117            if (cast.length() != 0 && !cast.equals("()")) {
1118                if (cast.startsWith("("))
1119                    cast = cast.substring(1);
1120                if (cast.endsWith(")"))
1121                    cast = cast.substring(0, cast.length() - 1);
1122                output(cast);
1123            } else {
1124                paramType = param.getType32();
1125                paramType64 = param.getType64();
1126                if (!(paramType.isPrimitive() || paramType.isArray())) {
1127                    if (param.getTypeClass().getFlag(ClassFlag.STRUCT) && !param.getTypeClass().getFlag(ClassFlag.TYPEDEF)) {
1128                        output("struct ");
1129                    }
1130                }
1131                output(paramType.getTypeSignature4(!paramType.equals(paramType64), param.getFlag(ArgFlag.BY_VALUE)));
1132            }
1133        }
1134        output("))");
1135        output(func);
1136        output(")");
1137    }
1138
1139    void generateReturn(JNIMethod method, JNIType returnType, boolean needsReturn) {
1140        if (needsReturn && !returnType.isType("void")) {
1141            outputln("\treturn rc;");
1142        }
1143    }
1144
1145    void generateMemmove(JNIMethod method, String function, String function64, List<JNIParameter> params) {
1146        generateEnterExitMacro(method, function, function64, true);
1147        output("\t");
1148        boolean get = params.get(0).getType32().isPrimitive();
1149        String className = params.get(get ? 1 : 0).getType32().getSimpleName();
1150        output(get ? "if (arg1) get" : "if (arg0) set");
1151        output(className);
1152        output(get ? "Fields(env, arg1, (" : "Fields(env, arg0, (");
1153        output(className);
1154        output(get ? " *)arg0)" : " *)arg1)");
1155        outputln(";");
1156        generateEnterExitMacro(method, function, function64, false);
1157    }
1158
1159    void generateFunctionBody(JNIMethod method, String function, String function64, List<JNIParameter> params, JNIType returnType, JNIType returnType64) {
1160        outputln("{");
1161
1162        /* Custom GTK memmoves. */
1163        String name = method.getName();
1164        if (name.startsWith("_"))
1165            name = name.substring(1);
1166        boolean isMemove = (name.equals("memmove") || name.equals("MoveMemory")) && params.size() == 2 && returnType.isType("void");
1167        if (isMemove) {
1168            generateMemmove(method, function, function64, params);
1169        } else {
1170            boolean needsReturn = generateLocalVars(method, params, returnType, returnType64);
1171            generateEnterExitMacro(method, function, function64, true);
1172            boolean genFailTag = generateGetters(method, params);
1173            if (method.getFlag(MethodFlag.DYNAMIC)) {
1174                generateDynamicFunctionCall(method, params, returnType, returnType64, needsReturn);
1175            } else {
1176                generateFunctionCall(method, params, returnType, returnType64, needsReturn);
1177            }
1178            if (genFailTag)
1179                outputln("fail:");
1180            generateSetters(method, params);
1181            generateEnterExitMacro(method, function, function64, false);
1182            generateReturn(method, returnType, needsReturn);
1183        }
1184
1185        outputln("}");
1186    }
1187
1188    void generateFunctionPrototype(JNIMethod method, String function, List<JNIParameter> params, JNIType returnType, JNIType returnType64, boolean singleLine) {
1189        output("JNIEXPORT ");
1190        output(returnType.getTypeSignature2(!returnType.equals(returnType64)));
1191        output(" JNICALL ");
1192        output(method.getDeclaringClass().getSimpleName());
1193        output("_NATIVE(");
1194        output(function);
1195        if (singleLine) {
1196            output(")");
1197            output("(JNIEnv *env, ");
1198        } else {
1199            outputln(")");
1200            output("\t(JNIEnv *env, ");
1201        }
1202        if ((method.getModifiers() & Modifier.STATIC) != 0) {
1203            output("jclass");
1204        } else {
1205            output("jobject");
1206        }
1207        output(" that");
1208        for (int i = 0; i < params.size(); i++) {
1209            output(", ");
1210            JNIType paramType = params.get(i).getType32(), paramType64 = params.get(i).getType64();
1211            output(paramType.getTypeSignature2(!paramType.equals(paramType64)));
1212            output(" arg" + i);
1213        }
1214        output(")");
1215        if (!singleLine)
1216            outputln();
1217    }
1218
1219    boolean isCritical(JNIParameter param) {
1220        JNIType paramType = param.getType32();
1221        return paramType.isArray() && paramType.getComponentType().isPrimitive() && param.getFlag(ArgFlag.CRITICAL);
1222    }
1223
1224    boolean isSystemClass(JNIType type) {
1225        return type.isType("java.lang.Object") || type.isType("java.lang.Class");
1226    }
1227
1228}