1 /*
   2  * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package com.apple.internal.jobjc.generator;
  26 
  27 import java.io.PrintStream;
  28 import java.io.StringWriter;
  29 
  30 import com.apple.internal.jobjc.generator.model.Arg;
  31 import com.apple.internal.jobjc.generator.model.Function;
  32 import com.apple.internal.jobjc.generator.model.Method;
  33 import com.apple.internal.jobjc.generator.model.types.JType;
  34 import com.apple.internal.jobjc.generator.utils.JavaLang.JLCall;
  35 import com.apple.internal.jobjc.generator.utils.JavaLang.JLField;
  36 import com.apple.internal.jobjc.generator.utils.JavaLang.JLMethod;
  37 import com.apple.internal.jobjc.generator.utils.JavaLang.JLTertiary;
  38 import com.apple.jobjc.Coder;
  39 import com.apple.jobjc.Invoke;
  40 import com.apple.jobjc.Invoke.FunCall;
  41 import com.apple.jobjc.Invoke.MsgSend;
  42 
  43 public class FunctionGenerator {
  44     private final static String VARARGS_NAME = "varargs";
  45 
  46     private static String createFieldCache(final Class<? extends Invoke> type, final Function fxn) {
  47         final String identifier = makeInstanceName(fxn);
  48 
  49         JLField field = new JLField("private static", type.getCanonicalName(), identifier);
  50         // It's okay to make it static, because the getter isn't static, so the only way to access it is through an instance.
  51         JLMethod getter = new JLMethod("private final", type.getCanonicalName(), "get_" + identifier);
  52 
  53         JLCall createIt = new JLCall("new " + type.getCanonicalName());
  54         createIt.args.add(firstArg(fxn));
  55         createIt.args.add("\"" + fxn.name + "\"");
  56         createIt.args.add(fxn.returnValue.type.getJType().getCoderDescriptor().getCoderInstanceName());
  57         for (final Arg arg : fxn.args)
  58             createIt.args.add(arg.type.getJType().getCoderDescriptor().getCoderInstanceName());
  59 
  60         getter.body.add("return " + new JLTertiary(identifier + " != null", identifier, identifier + " = " + createIt) + ";");
  61 
  62         return field.toString() + getter.toString();
  63     }
  64 
  65     private static String createLocalForward(final Class<? extends Invoke> type, final Function fxn) {
  66         final String identifier = makeInstanceName(fxn);
  67         return new JLField("final", type.getCanonicalName(), identifier, new JLCall("get_" + identifier)).toString();
  68     }
  69 
  70     private static String createLocalNew(final Class<? extends Invoke> type, final Function fxn) {
  71         final String identifier = makeInstanceName(fxn);
  72         StringWriter out = new StringWriter();
  73 
  74         out.append(String.format("%3$s[] argCoders = new %3$s[%1$d + %2$s.length];\n", fxn.args.size(), VARARGS_NAME, Coder.class.getCanonicalName()));
  75 
  76         for(int i = 0; i < fxn.args.size(); i++)
  77             out.append(String.format("argCoders[%1$d] = %2$s;\n", i, fxn.args.get(0).type.getJType().getCoderDescriptor().getCoderInstanceName()));
  78 
  79         if(fxn.variadic){
  80             out.append(String.format("for(int i = %1$d; i < (%1$d + %2$s.length); i++)\n", fxn.args.size(), VARARGS_NAME));
  81             out.append(String.format("\targCoders[i] = %1$s.getCoderAtRuntime(%2$s[i - %3$s]);\n", Coder.class.getCanonicalName(), VARARGS_NAME, fxn.args.size()));
  82         }
  83 
  84         out.append("final " + type.getCanonicalName() + " " + identifier + " = new " + type.getCanonicalName() + "(" + firstArg(fxn) + ", \"" + fxn.name + "\", "
  85                 + fxn.returnValue.type.getJType().getCoderDescriptor().getCoderInstanceName() + ", argCoders);");
  86 
  87         return out.toString();
  88     }
  89 
  90     private static final String CONTEXT_NAME = "nativeBuffer";
  91 
  92     public static void writeOutFunction(final PrintStream out, final Class<? extends Invoke> type, final Function fxn, final String initWithObj) {
  93         final String instName = makeInstanceName(fxn);
  94         final JType returnJavaType = fxn.returnValue.type.getJType();
  95 
  96         if(!fxn.variadic){
  97             out.print(createFieldCache(type, fxn));
  98             out.println();
  99         }
 100 
 101         JLMethod meth = new JLMethod("public", returnJavaType.getJavaReturnTypeName(), fxn.getJavaName());
 102 
 103         for(Arg arg : fxn.args)
 104             meth.args.add("final " + arg.type.getJType().getTypeNameAsParam() + " " + arg.javaName);
 105 
 106         if(fxn.variadic)
 107             meth.args.add("final Object... " + VARARGS_NAME);
 108 
 109         if(fxn instanceof Method && ((Method)fxn).ignore){
 110             String suggestion = ((Method)fxn).suggestion == null ? "" : (" Suggested work-around: " + ((Method)fxn).suggestion);
 111             meth.jdoc.add("@deprecated The framework recommends that this method be ignored. (It may be deprecated.)" + suggestion);
 112             meth.attrs.add("@Deprecated");
 113         }
 114 
 115         // type mismatch warning
 116         {
 117             {
 118                 String retMsg = fxn.returnValue.type.getJType().getCoderDescriptor().mismatchMessage();
 119                 if(retMsg != null){
 120                     meth.jdoc.add("@deprecated Possible type mismatch: (return value) " + retMsg);
 121                     meth.attrs.add("@Deprecated");
 122                 }
 123             }
 124 
 125             for(int i = 0; i < fxn.args.size(); i++){
 126                 final Arg arg = fxn.args.get(i);
 127                 String argMsg = arg.type.getJType().getCoderDescriptor().mismatchMessage();
 128                 if(argMsg != null){
 129                     meth.jdoc.add("@deprecated Possible type mismatch: (arg" + i + ": " + arg.javaName + ") " + argMsg);
 130                     meth.attrs.add("@Deprecated");
 131                 }
 132             }
 133         }
 134 
 135         if(fxn.variadic)
 136             meth.body.add(createLocalNew(coreType(fxn), fxn));
 137         else
 138             meth.body.add(createLocalForward(coreType(fxn), fxn));
 139 
 140         meth.body.add(returnJavaType.createDeclareBuffer(CONTEXT_NAME));
 141         meth.body.add(returnJavaType.createInit(CONTEXT_NAME, instName, initWithObj));
 142 
 143         for(final Arg arg : fxn.args)
 144             meth.body.add(arg.type.getJType().getCoderDescriptor().getPushStatementFor(CONTEXT_NAME, arg.javaName));
 145 
 146         if(fxn.variadic){
 147             meth.body.add(String.format("for(int i = %1$d; i < (%1$d + %2$s.length); i++)", fxn.args.size(), VARARGS_NAME));
 148             meth.body.add(String.format("\targCoders[i].push(%1$s, %2$s[i - %3$d]);", CONTEXT_NAME, VARARGS_NAME, fxn.args.size()));
 149         }
 150 
 151         meth.body.add(returnJavaType.createInvoke(CONTEXT_NAME, instName));
 152         meth.body.add(returnJavaType.createPop(CONTEXT_NAME));
 153         meth.body.add(returnJavaType.createReturn());
 154 
 155         out.print(meth.toString());
 156         out.println();
 157     }
 158 
 159     private static Class<? extends Invoke> coreType(final Function fxn){
 160         return fxn instanceof Method ? MsgSend.class : FunCall.class;
 161     }
 162 
 163     private static String firstArg(Function fxn){
 164         return fxn instanceof Method ? "getRuntime()" : "this";
 165     }
 166 
 167     private static String makeInstanceName(Function fxn){
 168         String ext;
 169         if(fxn instanceof Method){
 170             if(((Method) fxn).isClassMethod) ext = "CMetInst";
 171             else                             ext = "IMetInst";
 172         }
 173         else
 174             ext = "FxnInst";
 175 
 176         return fxn.getJavaName() + "_" + ext;
 177     }
 178 }