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