blob: 37f67aa39bdb303edd79ea40c9d2e765a0520401 [file] [log] [blame]
/*
* Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.apple.internal.jobjc.generator;
import java.io.PrintStream;
import java.io.StringWriter;
import com.apple.internal.jobjc.generator.model.Arg;
import com.apple.internal.jobjc.generator.model.Function;
import com.apple.internal.jobjc.generator.model.Method;
import com.apple.internal.jobjc.generator.model.types.JType;
import com.apple.internal.jobjc.generator.utils.JavaLang.JLCall;
import com.apple.internal.jobjc.generator.utils.JavaLang.JLField;
import com.apple.internal.jobjc.generator.utils.JavaLang.JLMethod;
import com.apple.internal.jobjc.generator.utils.JavaLang.JLTertiary;
import com.apple.jobjc.Coder;
import com.apple.jobjc.Invoke;
import com.apple.jobjc.Invoke.FunCall;
import com.apple.jobjc.Invoke.MsgSend;
public class FunctionGenerator {
private final static String VARARGS_NAME = "varargs";
private static String createFieldCache(final Class<? extends Invoke> type, final Function fxn) {
final String identifier = makeInstanceName(fxn);
JLField field = new JLField("private static", type.getCanonicalName(), identifier);
// It's okay to make it static, because the getter isn't static, so the only way to access it is through an instance.
JLMethod getter = new JLMethod("private final", type.getCanonicalName(), "get_" + identifier);
JLCall createIt = new JLCall("new " + type.getCanonicalName());
createIt.args.add(firstArg(fxn));
createIt.args.add("\"" + fxn.name + "\"");
createIt.args.add(fxn.returnValue.type.getJType().getCoderDescriptor().getCoderInstanceName());
for (final Arg arg : fxn.args)
createIt.args.add(arg.type.getJType().getCoderDescriptor().getCoderInstanceName());
getter.body.add("return " + new JLTertiary(identifier + " != null", identifier, identifier + " = " + createIt) + ";");
return field.toString() + getter.toString();
}
private static String createLocalForward(final Class<? extends Invoke> type, final Function fxn) {
final String identifier = makeInstanceName(fxn);
return new JLField("final", type.getCanonicalName(), identifier, new JLCall("get_" + identifier)).toString();
}
private static String createLocalNew(final Class<? extends Invoke> type, final Function fxn) {
final String identifier = makeInstanceName(fxn);
StringWriter out = new StringWriter();
out.append(String.format("%3$s[] argCoders = new %3$s[%1$d + %2$s.length];\n", fxn.args.size(), VARARGS_NAME, Coder.class.getCanonicalName()));
for(int i = 0; i < fxn.args.size(); i++)
out.append(String.format("argCoders[%1$d] = %2$s;\n", i, fxn.args.get(0).type.getJType().getCoderDescriptor().getCoderInstanceName()));
if(fxn.variadic){
out.append(String.format("for(int i = %1$d; i < (%1$d + %2$s.length); i++)\n", fxn.args.size(), VARARGS_NAME));
out.append(String.format("\targCoders[i] = %1$s.getCoderAtRuntime(%2$s[i - %3$s]);\n", Coder.class.getCanonicalName(), VARARGS_NAME, fxn.args.size()));
}
out.append("final " + type.getCanonicalName() + " " + identifier + " = new " + type.getCanonicalName() + "(" + firstArg(fxn) + ", \"" + fxn.name + "\", "
+ fxn.returnValue.type.getJType().getCoderDescriptor().getCoderInstanceName() + ", argCoders);");
return out.toString();
}
private static final String CONTEXT_NAME = "nativeBuffer";
public static void writeOutFunction(final PrintStream out, final Class<? extends Invoke> type, final Function fxn, final String initWithObj) {
final String instName = makeInstanceName(fxn);
final JType returnJavaType = fxn.returnValue.type.getJType();
if(!fxn.variadic){
out.print(createFieldCache(type, fxn));
out.println();
}
JLMethod meth = new JLMethod("public", returnJavaType.getJavaReturnTypeName(), fxn.getJavaName());
for(Arg arg : fxn.args)
meth.args.add("final " + arg.type.getJType().getTypeNameAsParam() + " " + arg.javaName);
if(fxn.variadic)
meth.args.add("final Object... " + VARARGS_NAME);
if(fxn instanceof Method && ((Method)fxn).ignore){
String suggestion = ((Method)fxn).suggestion == null ? "" : (" Suggested work-around: " + ((Method)fxn).suggestion);
meth.jdoc.add("@deprecated The framework recommends that this method be ignored. (It may be deprecated.)" + suggestion);
meth.attrs.add("@Deprecated");
}
// type mismatch warning
{
{
String retMsg = fxn.returnValue.type.getJType().getCoderDescriptor().mismatchMessage();
if(retMsg != null){
meth.jdoc.add("@deprecated Possible type mismatch: (return value) " + retMsg);
meth.attrs.add("@Deprecated");
}
}
for(int i = 0; i < fxn.args.size(); i++){
final Arg arg = fxn.args.get(i);
String argMsg = arg.type.getJType().getCoderDescriptor().mismatchMessage();
if(argMsg != null){
meth.jdoc.add("@deprecated Possible type mismatch: (arg" + i + ": " + arg.javaName + ") " + argMsg);
meth.attrs.add("@Deprecated");
}
}
}
if(fxn.variadic)
meth.body.add(createLocalNew(coreType(fxn), fxn));
else
meth.body.add(createLocalForward(coreType(fxn), fxn));
meth.body.add(returnJavaType.createDeclareBuffer(CONTEXT_NAME));
meth.body.add(returnJavaType.createInit(CONTEXT_NAME, instName, initWithObj));
for(final Arg arg : fxn.args)
meth.body.add(arg.type.getJType().getCoderDescriptor().getPushStatementFor(CONTEXT_NAME, arg.javaName));
if(fxn.variadic){
meth.body.add(String.format("for(int i = %1$d; i < (%1$d + %2$s.length); i++)", fxn.args.size(), VARARGS_NAME));
meth.body.add(String.format("\targCoders[i].push(%1$s, %2$s[i - %3$d]);", CONTEXT_NAME, VARARGS_NAME, fxn.args.size()));
}
meth.body.add(returnJavaType.createInvoke(CONTEXT_NAME, instName));
meth.body.add(returnJavaType.createPop(CONTEXT_NAME));
meth.body.add(returnJavaType.createReturn());
out.print(meth.toString());
out.println();
}
private static Class<? extends Invoke> coreType(final Function fxn){
return fxn instanceof Method ? MsgSend.class : FunCall.class;
}
private static String firstArg(Function fxn){
return fxn instanceof Method ? "getRuntime()" : "this";
}
private static String makeInstanceName(Function fxn){
String ext;
if(fxn instanceof Method){
if(((Method) fxn).isClassMethod) ext = "CMetInst";
else ext = "IMetInst";
}
else
ext = "FxnInst";
return fxn.getJavaName() + "_" + ext;
}
}