blob: 3b462ffec2b9c0c6196d16a0797b8aec2db74c28 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/* Generated By:JJTree: Do not edit this line. ASTFunDecl.java */
/* JJT: 0.3pre1 */
package Mini;
import java.io.PrintWriter;
import java.util.Iterator;
import org.apache.bcel.Const;
import org.apache.bcel.generic.ALOAD;
import org.apache.bcel.generic.ASTORE;
import org.apache.bcel.generic.ArrayType;
import org.apache.bcel.generic.BranchHandle;
import org.apache.bcel.generic.BranchInstruction;
import org.apache.bcel.generic.ClassGen;
import org.apache.bcel.generic.ConstantPoolGen;
import org.apache.bcel.generic.GETSTATIC;
import org.apache.bcel.generic.GOTO;
import org.apache.bcel.generic.INVOKEVIRTUAL;
import org.apache.bcel.generic.InstructionConst;
import org.apache.bcel.generic.InstructionHandle;
import org.apache.bcel.generic.InstructionList;
import org.apache.bcel.generic.InstructionTargeter;
import org.apache.bcel.generic.LocalVariableGen;
import org.apache.bcel.generic.MethodGen;
import org.apache.bcel.generic.ObjectType;
import org.apache.bcel.generic.TargetLostException;
import org.apache.bcel.generic.Type;
import org.apache.bcel.util.InstructionFinder;
/**
*/
public class ASTFunDecl extends SimpleNode implements MiniParserTreeConstants {
private static final InstructionFinder.CodeConstraint my_constraint = match -> {
final BranchInstruction if_icmp = (BranchInstruction) match[0].getInstruction();
final GOTO goto_ = (GOTO) match[2].getInstruction();
return if_icmp.getTarget() == match[3] && goto_.getTarget() == match[4];
};
/*
* Used to simpulate stack with local vars and compute maximum stack size.
*/
static int size, max_size;
private static String getVarDecls() {
final StringBuilder buf = new StringBuilder(" int ");
for (int i = 0; i < max_size; i++) {
buf.append("_s" + i);
if (i < max_size - 1) {
buf.append(", ");
}
}
buf.append(";\n");
return buf.toString();
}
public static Node jjtCreate(final MiniParser p, final int id) {
return new ASTFunDecl(p, id);
}
/**
* Replaces instruction sequences (typically generated by ASTIfExpr) of the form
*
* IF_ICMP__, ICONST_1, GOTO, ICONST_0, IFEQ, Instruction
*
* where the IF_ICMP__ branches to the ICONST_0 (else part) and the GOTO branches to the IFEQ with the simpler
* expression
*
* IF_ICMP__, Instruction
*
* where the IF_ICMP__ now branches to the target of the previous IFEQ instruction.
*/
private static void optimizeIFs(final InstructionList il) {
final InstructionFinder f = new InstructionFinder(il);
final String pat = "IF_ICMP ICONST_1 GOTO ICONST_0 IFEQ Instruction";
for (final Iterator<InstructionHandle[]> it = f.search(pat, my_constraint); it.hasNext();) {
final InstructionHandle[] match = it.next();
// Everything ok, update code
final BranchInstruction ifeq = (BranchInstruction) match[4].getInstruction();
final BranchHandle if_icmp = (BranchHandle) match[0];
if_icmp.setTarget(ifeq.getTarget());
try {
il.delete(match[1], match[4]);
} catch (final TargetLostException e) {
final InstructionHandle[] targets = e.getTargets();
System.err.println(targets[0]);
for (final InstructionHandle target : targets) {
final InstructionTargeter[] targeters = target.getTargeters();
for (final InstructionTargeter targeter : targeters) {
// TODO Is this really identity equality and not object equality?
if (target != match[4] || targeter != match[2]) {
System.err.println("Unexpected: " + e);
}
}
}
}
}
}
static String pop() {
return "_s" + --size;
}
/**
* Used by byte_code()
*/
static void pop(final int s) {
size -= s;
}
static void push() {
push(1);
}
static void push(final int s) {
size += s;
if (size > max_size) {
max_size = size;
}
}
/**
* Used byte code()
*/
static void push(final StringBuffer buf, final String str) {
buf.append(" _s" + size + " = " + str + ";\n");
push(1);
}
static void reset() {
size = max_size = 0;
}
private ASTIdent name;
private ASTIdent[] argv;
private ASTExpr body;
private int type = Const.T_UNKNOWN;
private int line, column;
private boolean is_simple; // true, if simple expression like '12 + f(a)'
private boolean is_recursive; // Not used yet, TODO
// private int max_depth; // max. expression tree depth
private Environment env;
ASTFunDecl(final ASTIdent name, final ASTIdent[] argv, final ASTExpr body, final int type) {
this(JJTFUNDECL);
this.name = name;
this.argv = argv;
this.body = body;
this.type = type;
}
// Generated methods
ASTFunDecl(final int id) {
super(id);
}
ASTFunDecl(final MiniParser p, final int id) {
super(p, id);
}
/**
* Fifth pass, produce Java byte code.
*/
public void byte_code(final ClassGen classGen, final ConstantPoolGen cp) {
MethodGen method = null;
boolean main = false, ignore = false;
final String className = classGen.getClassName();
final String fname = name.getName();
final InstructionList il = new InstructionList();
Type[] args = {new ArrayType(Type.STRING, 1)}; // default for 'main'
String[] arg_names = {"$argv"};
if (fname.equals("main")) {
method = new MethodGen(Const.ACC_STATIC | Const.ACC_PUBLIC, Type.VOID, args, arg_names, "main", className, il, cp);
main = true;
} else if (fname.equals("READ") || fname.equals("WRITE")) { // Do nothing
ignore = true;
} else {
final int size = argv.length;
arg_names = new String[size];
args = new Type[size];
for (int i = 0; i < size; i++) {
args[i] = Type.INT;
arg_names[i] = argv[i].getName();
}
method = new MethodGen(Const.ACC_STATIC | Const.ACC_PRIVATE | Const.ACC_FINAL, Type.INT, args, arg_names, fname, className, il, cp);
final LocalVariableGen[] lv = method.getLocalVariables();
for (int i = 0; i < size; i++) {
final Variable entry = (Variable) env.get(arg_names[i]);
entry.setLocalVariable(lv[i]);
}
method.addException("java.io.IOException");
}
if (!ignore) {
body.byte_code(il, method, cp);
if (main) {
final ObjectType eType = new ObjectType("java.lang.Exception");
final InstructionHandle start = il.getStart();
InstructionHandle end, handler, end_handler;
final LocalVariableGen exc = method.addLocalVariable("$e", eType, null, null);
final int slot = exc.getIndex();
il.append(InstructionConst.POP);
pop(); // Remove last element on stack
end = il.append(InstructionConst.RETURN); // Use instruction constants, if possible
// catch
handler = il.append(new ASTORE(slot)); // save exception object
il.append(new GETSTATIC(cp.addFieldref("java.lang.System", "err", "Ljava/io/PrintStream;")));
il.append(new ALOAD(slot));
push(2);
il.append(new INVOKEVIRTUAL(cp.addMethodref("java.io.PrintStream", "println", "(Ljava/lang/Object;)V")));
pop(2);
end_handler = il.append(InstructionConst.RETURN);
method.addExceptionHandler(start, end, handler, eType);
exc.setStart(handler);
exc.setEnd(end_handler);
} else {
il.append(InstructionConst.IRETURN); // Reuse object to save memory
}
method.removeNOPs(); // First optimization pass, provided by MethodGen
optimizeIFs(il); // Second optimization pass, application-specific
method.setMaxStack(max_size);
classGen.addMethod(method.getMethod());
}
il.dispose(); // Dispose instruction handles for better memory utilization
reset();
}
/**
* Overrides SimpleNode.closeNode() Cast children to appropriate type.
*/
@Override
public void closeNode() {
name = (ASTIdent) children[0];
body = (ASTExpr) children[children.length - 1];
argv = new ASTIdent[children.length - 2]; // May be 0-size array
for (int i = 1; i < children.length - 1; i++) {
argv[i - 1] = (ASTIdent) children[i];
}
children = null; // Throw away old reference
}
/**
* Fourth pass, produce Java code.
*/
public void code(final PrintWriter out) {
String expr;
boolean main = false, ignore = false;
final String fname = name.getName();
if (fname.equals("main")) {
out.println(" public static void main(String[] _argv) {");
main = true;
} else if (fname.equals("READ") || fname.equals("WRITE")) { // Do nothing
ignore = true;
} else {
out.print(" public static final " + "int" + // type_names[type] +
" " + fname + "(");
for (int i = 0; i < argv.length; i++) {
out.print("int " + argv[i].getName());
if (i < argv.length - 1) {
out.print(", ");
}
}
out.println(")\n throws IOException\n {");
}
if (!ignore) {
final StringBuffer buf = new StringBuffer();
body.code(buf);
out.println(getVarDecls());
expr = buf.toString();
if (main) {
out.println(" try {");
}
out.println(expr);
if (main) {
out.println(" } catch (Exception e) { System.err.println(e); }\n }\n");
} else {
out.println("\n return " + pop() + ";\n }\n");
}
}
reset();
}
/**
* Overrides SimpleNode.dump()
*/
@Override
public void dump(final String prefix) {
System.out.println(toString(prefix));
for (final ASTIdent element : argv) {
element.dump(prefix + " ");
}
body.dump(prefix + " ");
}
/**
* Second pass.
*
* @return type of expression.
*/
public int eval(final int pass) {
final int expected = name.getType(); // Maybe other function has already called us
type = body.eval(expected); // And updated the env
if (expected != Const.T_UNKNOWN && type != expected) {
MiniC.addError(line, column,
"Function f ist not of type " + Const.getTypeName(expected) + " as previously assumed, but " + Const.getTypeName(type));
}
name.setType(type);
is_simple = body.isSimple();
if (pass == 2 && type == Const.T_UNKNOWN) {
is_recursive = true;
}
return type;
}
public ASTIdent[] getArgs() {
return argv;
}
public int getColumn() {
return column;
}
public int getLine() {
return line;
}
public ASTIdent getName() {
return name;
}
public int getNoArgs() {
return argv.length;
}
public int getType() {
return type;
}
public boolean isrecursive() {
return is_recursive;
}
public boolean isSimple() {
return is_simple;
}
public void setColumn(final int column) {
this.column = column;
}
public void setLine(final int line) {
this.line = line;
}
public void setPosition(final int line, final int column) {
this.line = line;
this.column = column;
}
public void setType(final int type) {
this.type = type;
}
/**
* Overrides SimpleNode.toString()
*/
@Override
public String toString() {
final StringBuilder buf = new StringBuilder();
buf.append(jjtNodeName[id] + " " + name + "(");
for (int i = 0; i < argv.length; i++) {
buf.append(argv[i].getName());
if (i < argv.length - 1) {
buf.append(", ");
}
}
buf.append(")");
return buf.toString();
}
/**
* First pass of parse tree.
*/
public ASTFunDecl traverse(final Environment env) {
this.env = env;
// Put arguments into hash table aka environment
for (final ASTIdent element : argv) {
final EnvEntry entry = env.get(element.getName());
if (entry != null) {
MiniC.addError(element.getLine(), element.getColumn(), "Redeclaration of " + entry + ".");
} else {
env.put(new Variable(element));
}
}
/*
* Update entry of this function, that is, sets argument references. The entry is already in there by garantee, but may be
* of the wrong type: The user-defined a function 'TRUE', for example and 'TRUE' is of type 'Variable'.
*/
try {
final Function fun = (Function) env.get(name.getName());
fun.setArgs(argv);
} catch (final ClassCastException e) {
} // Who cares?
body = body.traverse(env); // Traverse expression body
return this;
}
}