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     *******************************************************************************/
011    package org.fusesource.hawtjni.generator;
012    
013    import java.lang.reflect.Modifier;
014    import java.util.ArrayList;
015    import java.util.List;
016    
017    import org.fusesource.hawtjni.generator.model.JNIClass;
018    import org.fusesource.hawtjni.generator.model.JNIField;
019    import org.fusesource.hawtjni.generator.model.JNIMethod;
020    import org.fusesource.hawtjni.generator.model.JNIParameter;
021    import org.fusesource.hawtjni.generator.model.JNIType;
022    import org.fusesource.hawtjni.runtime.ArgFlag;
023    import org.fusesource.hawtjni.runtime.ClassFlag;
024    import org.fusesource.hawtjni.runtime.FieldFlag;
025    import org.fusesource.hawtjni.runtime.MethodFlag;
026    
027    import static org.fusesource.hawtjni.runtime.MethodFlag.*;
028    
029    /**
030     * 
031     * @author <a href="http://hiramchirino.com">Hiram Chirino</a>
032     */
033    public 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("java.lang.String")) {
600                    if (param.getFlag(ArgFlag.UNICODE)) {
601                        output("const jchar *lparg" + i);
602                    } else {
603                        output("const char *lparg" + i);
604                    }
605                    output("= NULL;");
606                } else {
607                    if (param.getTypeClass().getFlag(ClassFlag.STRUCT) && !param.getTypeClass().getFlag(ClassFlag.TYPEDEF)) {
608                        output("struct ");
609                    }
610                    output(paramType.getNativeName());
611                    output(" _arg" + i);
612                    if (param.getFlag(ArgFlag.INIT))
613                        output("={0}");
614                    output(", *lparg" + i);
615                    output("=NULL;");
616                }
617                outputln();
618                needsReturn = true;
619            }
620            if (needsReturn) {
621                if (!returnType.isType("void")) {
622                    output("\t");
623                    output(returnType.getTypeSignature2(!returnType.equals(returnType64)));
624                    outputln(" rc = 0;");
625                }
626            }
627            return needsReturn;
628        }
629    
630        boolean generateGetters(JNIMethod method, List<JNIParameter> params) {
631            boolean genFailTag = false;
632            int criticalCount = 0;
633            for (JNIParameter param : params) {
634                if (!isCritical(param)) {
635                    genFailTag |= generateGetParameter(method, param, false, 1);
636                } else {
637                    criticalCount++;
638                }
639            }
640            if (criticalCount != 0) {
641                outputln("#ifdef JNI_VERSION_1_2");
642                outputln("\tif (IS_JNI_1_2) {");
643                for (JNIParameter param : params) {
644                    if (isCritical(param)) {
645                        genFailTag |= generateGetParameter(method, param, true, 2);
646                    }
647                }
648                outputln("\t} else");
649                outputln("#endif");
650                outputln("\t{");
651                for (JNIParameter param : params) {
652                    if (isCritical(param)) {
653                        genFailTag |= generateGetParameter(method, param, false, 2);
654                    }
655                }
656                outputln("\t}");
657            }
658            return genFailTag;
659        }
660    
661        void generateSetters(JNIMethod method, List<JNIParameter> params) {
662            int criticalCount = 0;
663            for (int i = params.size() - 1; i >= 0; i--) {
664                JNIParameter param = params.get(i);
665                if (isCritical(param)) {
666                    criticalCount++;
667                }
668            }
669            if (criticalCount != 0) {
670                outputln("#ifdef JNI_VERSION_1_2");
671                outputln("\tif (IS_JNI_1_2) {");
672                for (int i = params.size() - 1; i >= 0; i--) {
673                    JNIParameter param = params.get(i);
674                    if (isCritical(param)) {
675                        output("\t");
676                        generateSetParameter(param, true);
677                    }
678                }
679                outputln("\t} else");
680                outputln("#endif");
681                outputln("\t{");
682                for (int i = params.size() - 1; i >= 0; i--) {
683                    JNIParameter param = params.get(i);
684                    if (isCritical(param)) {
685                        output("\t");
686                        generateSetParameter(param, false);
687                    }
688                }
689                outputln("\t}");
690            }
691            for (int i = params.size() - 1; i >= 0; i--) {
692                JNIParameter param = params.get(i);
693                if (!isCritical(param)) {
694                    generateSetParameter(param, false);
695                }
696            }
697        }
698    
699        void generateDynamicFunctionCall(JNIMethod method, List<JNIParameter> params, JNIType returnType, JNIType returnType64, boolean needsReturn) {
700            outputln("/*");
701            generateFunctionCall(method, params, returnType, returnType64, needsReturn);
702            outputln("*/");
703            outputln("\t{");
704    
705            String name = method.getName();
706            if (name.startsWith("_"))
707                name = name.substring(1);
708            output("\t\tLOAD_FUNCTION(fp, ");
709            output(name);
710            outputln(")");
711            outputln("\t\tif (fp) {");
712            output("\t\t");
713            generateFunctionCallLeftSide(method, returnType, returnType64, needsReturn);
714            output("((");
715            output(returnType.getTypeSignature2(!returnType.equals(returnType64)));
716            output(" (CALLING_CONVENTION*)(");
717            for (int i = 0; i < params.size(); i++) {
718                if (i != 0)
719                    output(", ");
720                JNIParameter param = params.get(i);
721                String cast = param.getCast();
722                if( param.isPointer() ) {
723                    output("(intptr_t)");
724                }
725                boolean isStruct = param.getFlag(ArgFlag.BY_VALUE);
726                if (cast.length() > 2) {
727                    cast = cast.substring(1, cast.length() - 1);
728                    if (isStruct) {
729                        int index = cast.lastIndexOf('*');
730                        if (index != -1)
731                            cast = cast.substring(0, index).trim();
732                    }
733                    output(cast);
734                } else {
735                    JNIType paramType = param.getType32(), paramType64 = param.getType64();
736                    output(paramType.getTypeSignature4(!paramType.equals(paramType64), isStruct));
737                }
738            }
739            output("))");
740            output("fp");
741            output(")");
742            generateFunctionCallRightSide(method, params, 0);
743            output(";");
744            outputln();
745            outputln("\t\t}");
746            outputln("\t}");
747        }
748    
749        void generateFunctionCallLeftSide(JNIMethod method, JNIType returnType, JNIType returnType64, boolean needsReturn) {
750            output("\t");
751            if (!returnType.isType("void")) {
752                if (needsReturn) {
753                    output("rc = ");
754                } else {
755                    output("return ");
756                }
757    
758                String cast = method.getCast();
759                if (cast.length() != 0 && !cast.equals("()")) {
760                    if( method.isPointer() ) {
761                        output("(intptr_t)");
762                    }
763                    output(cast);
764                } else {
765                    if( method.getFlag(CPP_NEW)) {
766                        String[] parts = getNativeNameParts(method);
767                        String className = parts[0];
768                        output("(intptr_t)("+className+" *)");
769                    } else {
770                        output("(");
771                        output(returnType.getTypeSignature2(!returnType.equals(returnType64)));
772                        output(")");
773                    }
774                }
775            }
776            if (method.getFlag(MethodFlag.ADDRESS)) {
777                output("&");
778            }
779            if (method.getFlag(MethodFlag.JNI)) {
780                output(isCPP ? "env->" : "(*env)->");
781            }
782        }
783    
784        void generateFunctionCallRightSide(JNIMethod method, List<JNIParameter> params, int paramStart) {
785            if (!method.getFlag(MethodFlag.CONSTANT_GETTER)) {
786                output("(");
787                if (method.getFlag(MethodFlag.JNI)) {
788                    if (!isCPP)
789                        output("env, ");
790                }
791                for (int i = paramStart; i < params.size(); i++) {
792                    JNIParameter param = params.get(i);
793                    if (i != paramStart)
794                        output(", ");
795                    if (param.getFlag(ArgFlag.BY_VALUE))
796                        output("*");
797                    output(param.getCast());
798                    if( param.isPointer() ) {
799                        output("(intptr_t)");
800                    }
801                    if (param.getFlag(ArgFlag.CS_OBJECT))
802                        output("TO_OBJECT(");
803                    if (i == params.size() - 1 && param.getFlag(ArgFlag.SENTINEL)) {
804                        output("NULL");
805                    } else {
806                        JNIType paramType = param.getType32();
807                        if (!paramType.isPrimitive() && !isSystemClass(paramType))
808                            output("lp");
809                        output("arg" + i);
810                    }
811                    if (param.getFlag(ArgFlag.CS_OBJECT))
812                        output(")");
813                }
814                output(")");
815            }
816        }
817    
818        static String[] getNativeNameParts(JNIMethod method) {
819            String className = null;
820            String methodName = null;
821    
822            JNIClass dc = method.getDeclaringClass();
823            if( dc.getFlag(ClassFlag.CPP) || dc.getFlag(ClassFlag.STRUCT) ) {
824                className = method.getDeclaringClass().getNativeName();
825            }
826    
827            if( method.getAccessor().length() != 0 ) {
828                methodName = method.getAccessor();
829                int pos = methodName.lastIndexOf("::");
830                if( pos >= 0 ) {
831                    className = methodName.substring(0, pos);
832                    methodName = methodName.substring(pos+2);
833                }
834            } else {
835                methodName = method.getName();
836                if( className==null ) {
837                    int pos = methodName.indexOf("_");
838                    if( pos > 0 ) {
839                        className = methodName.substring(0, pos);
840                        methodName = methodName.substring(pos+1);
841                    }
842                }
843            }
844            if( className==null ) {
845                throw new Error(String.format("Could not determine object type name of method '%s'", method.getDeclaringClass().getSimpleName()+"."+method.getName()));
846            }
847            return new String[]{className, methodName};
848        }
849    
850        void generateFunctionCall(JNIMethod method, List<JNIParameter> params, JNIType returnType, JNIType returnType64, boolean needsReturn) {
851            String name = method.getName();
852            String copy = method.getCopy();
853            boolean makeCopy = copy.length() != 0 && isCPP && !returnType.isType("void");
854            if (makeCopy) {
855                output("\t{");
856                output("\t\t");
857                output(copy);
858                output(" temp = ");
859            } else {
860                generateFunctionCallLeftSide(method, returnType, returnType64, needsReturn);
861            }
862            int paramStart = 0;
863            if (name.startsWith("_"))
864                name = name.substring(1);
865    
866            boolean objc_struct = false;
867            if (name.equals("objc_msgSend_stret") || name.equals("objc_msgSendSuper_stret"))
868                objc_struct = true;
869            if (objc_struct) {
870                outputln("if (sizeof(_arg0) > STRUCT_SIZE_LIMIT) {");
871                generate_objc_msgSend_stret(method, params, name);
872                paramStart = 1;
873            } else if (name.equalsIgnoreCase("call")) {
874                output("(");
875                JNIParameter param = params.get(0);
876                String cast = param.getCast();
877                if (cast.length() != 0 && !cast.equals("()")) {
878                    output(cast);
879                    if( param.isPointer() ) {
880                        output("(intptr_t)");
881                    }
882                } else {
883                    output("(");
884                    output(returnType.getTypeSignature2(!returnType.equals(returnType64)));
885                    output(" (*)())");
886                }
887                output("arg0)");
888                paramStart = 1;
889            } else if (name.startsWith("VtblCall") || name.startsWith("_VtblCall")) {
890                output("((");
891                output(returnType.getTypeSignature2(!returnType.equals(returnType64)));
892                output(" (STDMETHODCALLTYPE *)(");
893                for (int i = 1; i < params.size(); i++) {
894                    if (i != 1)
895                        output(", ");
896                    JNIParameter param = params.get(i);
897                    JNIType paramType = param.getType32(), paramType64 = param.getType64();
898                    output(paramType.getTypeSignature4(!paramType.equals(paramType64), false));
899                }
900                output("))(*(");
901                JNIType paramType = params.get(1).getType32(), paramType64 = params.get(1).getType64();
902                output(paramType.getTypeSignature4(!paramType.equals(paramType64), false));
903                output(" **)arg1)[arg0])");
904                paramStart = 1;
905            } else if (method.getFlag(MethodFlag.CPP_METHOD) || method.getFlag(MethodFlag.SETTER) || method.getFlag(MethodFlag.GETTER) || method.getFlag(MethodFlag.ADDER)) {
906    
907                String[] parts = getNativeNameParts(method);
908                String className = parts[0];
909                String methodName = parts[1];
910    
911                if (method.getFlag(MethodFlag.CS_OBJECT)) {
912                    output("TO_HANDLE(");
913                }
914                output("(");
915                if( params.isEmpty() ) {
916                    throw new Error(String.format("C++ bound method '%s' missing the 'this' parameter", method.getDeclaringClass().getSimpleName()+"."+method.getName()));
917                }
918                JNIParameter param = params.get(0);
919                if (param.getFlag(ArgFlag.BY_VALUE))
920                    output("*");
921                String cast = param.getCast();
922                if (cast.length() != 0 && !cast.equals("()")) {
923                    output(cast);
924                    if( param.isPointer() ) {
925                        output("(intptr_t)");
926                    }
927                } else {
928                    output("("+className+" *)(intptr_t)");
929                }
930                if (param.getFlag(ArgFlag.CS_OBJECT)) {
931                    output("TO_OBJECT(");
932                }
933                output("arg0");
934                if (param.getFlag(ArgFlag.CS_OBJECT)) {
935                    output(")");
936                }
937                output(")->");
938                output(methodName);
939                paramStart = 1;
940            } else if (method.getFlag(MethodFlag.CS_NEW)) {
941                output("TO_HANDLE(gcnew ");
942                String accessor = method.getAccessor();
943                if (accessor.length() != 0) {
944                    output(accessor);
945                } else {
946                    JNIClass dc = method.getDeclaringClass();
947                    if( dc.getFlag(ClassFlag.CPP) || dc.getFlag(ClassFlag.STRUCT) ) {
948                        output(dc.getNativeName());
949                    } else {
950                        int index = -1;
951                        if ((index = name.indexOf('_')) != -1) {
952                            output(name.substring(index + 1));
953                        } else {
954                            output(name);
955                        }
956                    }
957                }
958            } else if (method.getFlag(MethodFlag.CPP_NEW)) {
959                if (method.getFlag(MethodFlag.CS_OBJECT)) {
960                    output("TO_HANDLE(");
961                }
962                output("new ");
963                String accessor = method.getAccessor();
964                if (accessor.length() != 0) {
965                    output(accessor);
966                } else {
967    
968                    JNIClass dc = method.getDeclaringClass();
969                    if( dc.getFlag(ClassFlag.CPP) ) {
970                        output(method.getDeclaringClass().getNativeName());
971                    } else {
972                        int index = -1;
973                        if ((index = name.indexOf('_')) != -1) {
974                            output(name.substring(index+1));
975                        } else {
976                            output(name);
977                        }
978                    }
979    
980                }
981            } else if (method.getFlag(MethodFlag.CPP_DELETE)) {
982                String[] parts = getNativeNameParts(method);
983                String className = parts[0];
984    
985                output("delete ");
986                JNIParameter param = params.get(0);
987                String cast = param.getCast();
988                if (cast.length() != 0 && !cast.equals("()")) {
989                    output(cast);
990                    if( param.isPointer() ) {
991                        output("(intptr_t)");
992                    }
993                } else {
994                    output("("+className+" *)(intptr_t)");
995                }
996                outputln("arg0;");
997                return;
998            } else {
999                if (method.getFlag(MethodFlag.CS_OBJECT)) {
1000                    output("TO_HANDLE(");
1001                }
1002                if (method.getFlag(MethodFlag.CAST)) {
1003                    output("((");
1004                    String returnCast = returnType.getTypeSignature2(!returnType.equals(returnType64));
1005                    if (name.equals("objc_msgSend_bool") && returnCast.equals("jboolean")) {
1006                        returnCast = "BOOL";
1007                    }
1008                    output(returnCast);
1009                    output(" (*)(");
1010                    for (int i = 0; i < params.size(); i++) {
1011                        if (i != 0)
1012                            output(", ");
1013                        JNIParameter param = params.get(i);
1014                        String cast = param.getCast();
1015                        if (cast.length() != 0 && !cast.equals("()") ) {
1016                            if (cast.startsWith("("))
1017                                cast = cast.substring(1);
1018                            if (cast.endsWith(")"))
1019                                cast = cast.substring(0, cast.length() - 1);
1020                            output(cast);
1021                        } else {
1022                            JNIType paramType = param.getType32(), paramType64 = param.getType64();
1023                            if (!(paramType.isPrimitive() || paramType.isArray())) {
1024                                if (param.getTypeClass().getFlag(ClassFlag.STRUCT) && !param.getTypeClass().getFlag(ClassFlag.TYPEDEF)) {
1025                                    output("struct ");
1026                                }
1027                            }
1028                            output(paramType.getTypeSignature4(!paramType.equals(paramType64), param.getFlag(ArgFlag.BY_VALUE)));
1029                        }
1030                    }
1031                    output("))");
1032                }
1033                String accessor = method.getAccessor();
1034                if (accessor.length() != 0) {
1035                    output(accessor);
1036                } else {
1037                    output(name);
1038                }
1039                if (method.getFlag(MethodFlag.CAST)) {
1040                    output(")");
1041                }
1042            }
1043            if ((method.getFlag(MethodFlag.SETTER) && params.size() == 3) || (method.getFlag(MethodFlag.GETTER) && params.size() == 2)) {
1044                output("[arg1]");
1045                paramStart++;
1046            }
1047            if (method.getFlag(MethodFlag.SETTER))
1048                output(" = ");
1049            if (method.getFlag(MethodFlag.ADDER))
1050                output(" += ");
1051            if (!method.getFlag(MethodFlag.GETTER)) {
1052                generateFunctionCallRightSide(method, params, paramStart);
1053            }
1054            if (method.getFlag(MethodFlag.CS_NEW) || method.getFlag(MethodFlag.CS_OBJECT)) {
1055                output(")");
1056            }
1057            output(";");
1058            outputln();
1059            if (makeCopy) {
1060                outputln("\t\t{");
1061                output("\t\t\t");
1062                output(copy);
1063                output("* copy = new ");
1064                output(copy);
1065                outputln("();");
1066                outputln("\t\t\t*copy = temp;");
1067                output("\t\t\trc = ");
1068                output("(");
1069                output(returnType.getTypeSignature2(!returnType.equals(returnType64)));
1070                output(")");
1071                outputln("copy;");
1072                outputln("\t\t}");
1073                outputln("\t}");
1074            }
1075            if (objc_struct) {
1076                outputln("\t} else {");
1077                generate_objc_msgSend_stret(method, params, name.substring(0, name.length() - "_stret".length()));
1078                generateFunctionCallRightSide(method, params, 1);
1079                outputln(";");
1080                outputln("\t}");
1081            }
1082        }
1083    
1084        void generate_objc_msgSend_stret(JNIMethod method, List<JNIParameter> params, String func) {
1085            output("\t\t*lparg0 = (*(");
1086            JNIType paramType = params.get(0).getType32(), paramType64 = params.get(0).getType64();
1087            output(paramType.getTypeSignature4(!paramType.equals(paramType64), true));
1088            output(" (*)(");
1089            for (int i = 1; i < params.size(); i++) {
1090                if (i != 1)
1091                    output(", ");
1092                JNIParameter param = params.get(i);
1093                String cast = param.getCast();
1094                if( param.isPointer() ) {
1095                    output("(intptr_t)");
1096                }
1097                if (cast.length() != 0 && !cast.equals("()")) {
1098                    if (cast.startsWith("("))
1099                        cast = cast.substring(1);
1100                    if (cast.endsWith(")"))
1101                        cast = cast.substring(0, cast.length() - 1);
1102                    output(cast);
1103                } else {
1104                    paramType = param.getType32();
1105                    paramType64 = param.getType64();
1106                    if (!(paramType.isPrimitive() || paramType.isArray())) {
1107                        if (param.getTypeClass().getFlag(ClassFlag.STRUCT) && !param.getTypeClass().getFlag(ClassFlag.TYPEDEF)) {
1108                            output("struct ");
1109                        }
1110                    }
1111                    output(paramType.getTypeSignature4(!paramType.equals(paramType64), param.getFlag(ArgFlag.BY_VALUE)));
1112                }
1113            }
1114            output("))");
1115            output(func);
1116            output(")");
1117        }
1118    
1119        void generateReturn(JNIMethod method, JNIType returnType, boolean needsReturn) {
1120            if (needsReturn && !returnType.isType("void")) {
1121                outputln("\treturn rc;");
1122            }
1123        }
1124    
1125        void generateMemmove(JNIMethod method, String function, String function64, List<JNIParameter> params) {
1126            generateEnterExitMacro(method, function, function64, true);
1127            output("\t");
1128            boolean get = params.get(0).getType32().isPrimitive();
1129            String className = params.get(get ? 1 : 0).getType32().getSimpleName();
1130            output(get ? "if (arg1) get" : "if (arg0) set");
1131            output(className);
1132            output(get ? "Fields(env, arg1, (" : "Fields(env, arg0, (");
1133            output(className);
1134            output(get ? " *)arg0)" : " *)arg1)");
1135            outputln(";");
1136            generateEnterExitMacro(method, function, function64, false);
1137        }
1138    
1139        void generateFunctionBody(JNIMethod method, String function, String function64, List<JNIParameter> params, JNIType returnType, JNIType returnType64) {
1140            outputln("{");
1141    
1142            /* Custom GTK memmoves. */
1143            String name = method.getName();
1144            if (name.startsWith("_"))
1145                name = name.substring(1);
1146            boolean isMemove = (name.equals("memmove") || name.equals("MoveMemory")) && params.size() == 2 && returnType.isType("void");
1147            if (isMemove) {
1148                generateMemmove(method, function, function64, params);
1149            } else {
1150                boolean needsReturn = generateLocalVars(method, params, returnType, returnType64);
1151                generateEnterExitMacro(method, function, function64, true);
1152                boolean genFailTag = generateGetters(method, params);
1153                if (method.getFlag(MethodFlag.DYNAMIC)) {
1154                    generateDynamicFunctionCall(method, params, returnType, returnType64, needsReturn);
1155                } else {
1156                    generateFunctionCall(method, params, returnType, returnType64, needsReturn);
1157                }
1158                if (genFailTag)
1159                    outputln("fail:");
1160                generateSetters(method, params);
1161                generateEnterExitMacro(method, function, function64, false);
1162                generateReturn(method, returnType, needsReturn);
1163            }
1164    
1165            outputln("}");
1166        }
1167    
1168        void generateFunctionPrototype(JNIMethod method, String function, List<JNIParameter> params, JNIType returnType, JNIType returnType64, boolean singleLine) {
1169            output("JNIEXPORT ");
1170            output(returnType.getTypeSignature2(!returnType.equals(returnType64)));
1171            output(" JNICALL ");
1172            output(method.getDeclaringClass().getSimpleName());
1173            output("_NATIVE(");
1174            output(function);
1175            if (singleLine) {
1176                output(")");
1177                output("(JNIEnv *env, ");
1178            } else {
1179                outputln(")");
1180                output("\t(JNIEnv *env, ");
1181            }
1182            if ((method.getModifiers() & Modifier.STATIC) != 0) {
1183                output("jclass");
1184            } else {
1185                output("jobject");
1186            }
1187            output(" that");
1188            for (int i = 0; i < params.size(); i++) {
1189                output(", ");
1190                JNIType paramType = params.get(i).getType32(), paramType64 = params.get(i).getType64();
1191                output(paramType.getTypeSignature2(!paramType.equals(paramType64)));
1192                output(" arg" + i);
1193            }
1194            output(")");
1195            if (!singleLine)
1196                outputln();
1197        }
1198    
1199        boolean isCritical(JNIParameter param) {
1200            JNIType paramType = param.getType32();
1201            return paramType.isArray() && paramType.getComponentType().isPrimitive() && param.getFlag(ArgFlag.CRITICAL);
1202        }
1203    
1204        boolean isSystemClass(JNIType type) {
1205            return type.isType("java.lang.Object") || type.isType("java.lang.Class");
1206        }
1207    
1208    }