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