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 }