/* * Copyright (c) 1999, 2013, 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.sun.tools.javac.jvm; import com.sun.tools.javac.code.*; import com.sun.tools.javac.code.Symbol.*; import com.sun.tools.javac.code.Type.*; import com.sun.tools.javac.jvm.Code.*; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.util.Assert; import static com.sun.tools.javac.jvm.ByteCodes.*; /** A helper class for code generation. Items are objects * that stand for addressable entities in the bytecode. Each item * supports a fixed protocol for loading the item on the stack, storing * into it, converting it into a jump condition, and several others. * There are many individual forms of items, such as local, static, * indexed, or instance variables, values on the top of stack, the * special values this or super, etc. Individual items are represented as * inner classes in class Items. * * <p><b>This is NOT part of any supported API. * If you write code that depends on this, you do so at your own risk. * This code and its internal interfaces are subject to change or * deletion without notice.</b> */ public class Items { /** The current constant pool. */ Pool pool; /** The current code buffer. */ Code code; /** The current symbol table. */ Symtab syms; /** Type utilities. */ Types types; /** Items that exist only once (flyweight pattern). */ private final Item voidItem; private final Item thisItem; private final Item superItem; private final Item[] stackItem = new Item[TypeCodeCount]; public Items(Pool pool, Code code, Symtab syms, Types types) { this.code = code; this.pool = pool; this.types = types; voidItem = new Item(VOIDcode) { public String toString() { return "void"; } }; thisItem = new SelfItem(false); superItem = new SelfItem(true); for (int i = 0; i < VOIDcode; i++) stackItem[i] = new StackItem(i); stackItem[VOIDcode] = voidItem; this.syms = syms; } /** Make a void item */ Item makeVoidItem() { return voidItem; } /** Make an item representing `this'. */ Item makeThisItem() { return thisItem; } /** Make an item representing `super'. */ Item makeSuperItem() { return superItem; } /** Make an item representing a value on stack. * @param type The value's type. */ Item makeStackItem(Type type) { return stackItem[Code.typecode(type)]; } /** Make an item representing a dynamically invoked method. * @param member The represented symbol. */ Item makeDynamicItem(Symbol member) { return new DynamicItem(member); } /** Make an item representing an indexed expression. * @param type The expression's type. */ Item makeIndexedItem(Type type) { return new IndexedItem(type); } /** Make an item representing a local variable. * @param v The represented variable. */ LocalItem makeLocalItem(VarSymbol v) { return new LocalItem(v.erasure(types), v.adr); } /** Make an item representing a local anonymous variable. * @param type The represented variable's type. * @param reg The represented variable's register. */ private LocalItem makeLocalItem(Type type, int reg) { return new LocalItem(type, reg); } /** Make an item representing a static variable or method. * @param member The represented symbol. */ Item makeStaticItem(Symbol member) { return new StaticItem(member); } /** Make an item representing an instance variable or method. * @param member The represented symbol. * @param nonvirtual Is the reference not virtual? (true for constructors * and private members). */ Item makeMemberItem(Symbol member, boolean nonvirtual) { return new MemberItem(member, nonvirtual); } /** Make an item representing a literal. * @param type The literal's type. * @param value The literal's value. */ Item makeImmediateItem(Type type, Object value) { return new ImmediateItem(type, value); } /** Make an item representing an assignment expression. * @param lhs The item representing the assignment's left hand side. */ Item makeAssignItem(Item lhs) { return new AssignItem(lhs); } /** Make an item representing a conditional or unconditional jump. * @param opcode The jump's opcode. * @param trueJumps A chain encomassing all jumps that can be taken * if the condition evaluates to true. * @param falseJumps A chain encomassing all jumps that can be taken * if the condition evaluates to false. */ CondItem makeCondItem(int opcode, Chain trueJumps, Chain falseJumps) { return new CondItem(opcode, trueJumps, falseJumps); } /** Make an item representing a conditional or unconditional jump. * @param opcode The jump's opcode. */ CondItem makeCondItem(int opcode) { return makeCondItem(opcode, null, null); } /** The base class of all items, which implements default behavior. */ abstract class Item { /** The type code of values represented by this item. */ int typecode; Item(int typecode) { this.typecode = typecode; } /** Generate code to load this item onto stack. */ Item load() { throw new AssertionError(); } /** Generate code to store top of stack into this item. */ void store() { throw new AssertionError("store unsupported: " + this); } /** Generate code to invoke method represented by this item. */ Item invoke() { throw new AssertionError(this); } /** Generate code to use this item twice. */ void duplicate() {} /** Generate code to avoid having to use this item. */ void drop() {} /** Generate code to stash a copy of top of stack - of typecode toscode - * under this item. */ void stash(int toscode) { stackItem[toscode].duplicate(); } /** Generate code to turn item into a testable condition. */ CondItem mkCond() { load(); return makeCondItem(ifne); } /** Generate code to coerce item to given type code. * @param targetcode The type code to coerce to. */ Item coerce(int targetcode) { if (typecode == targetcode) return this; else { load(); int typecode1 = Code.truncate(typecode); int targetcode1 = Code.truncate(targetcode); if (typecode1 != targetcode1) { int offset = targetcode1 > typecode1 ? targetcode1 - 1 : targetcode1; code.emitop0(i2l + typecode1 * 3 + offset); } if (targetcode != targetcode1) { code.emitop0(int2byte + targetcode - BYTEcode); } return stackItem[targetcode]; } } /** Generate code to coerce item to given type. * @param targettype The type to coerce to. */ Item coerce(Type targettype) { return coerce(Code.typecode(targettype)); } /** Return the width of this item on stack as a number of words. */ int width() { return 0; } public abstract String toString(); } /** An item representing a value on stack. */ class StackItem extends Item { StackItem(int typecode) { super(typecode); } Item load() { return this; } void duplicate() { code.emitop0(width() == 2 ? dup2 : dup); } void drop() { code.emitop0(width() == 2 ? pop2 : pop); } void stash(int toscode) { code.emitop0( (width() == 2 ? dup_x2 : dup_x1) + 3 * (Code.width(toscode) - 1)); } int width() { return Code.width(typecode); } public String toString() { return "stack(" + typecodeNames[typecode] + ")"; } } /** An item representing an indexed expression. */ class IndexedItem extends Item { IndexedItem(Type type) { super(Code.typecode(type)); } Item load() { code.emitop0(iaload + typecode); return stackItem[typecode]; } void store() { code.emitop0(iastore + typecode); } void duplicate() { code.emitop0(dup2); } void drop() { code.emitop0(pop2); } void stash(int toscode) { code.emitop0(dup_x2 + 3 * (Code.width(toscode) - 1)); } int width() { return 2; } public String toString() { return "indexed(" + ByteCodes.typecodeNames[typecode] + ")"; } } /** An item representing `this' or `super'. */ class SelfItem extends Item { /** Flag which determines whether this item represents `this' or `super'. */ boolean isSuper; SelfItem(boolean isSuper) { super(OBJECTcode); this.isSuper = isSuper; } Item load() { code.emitop0(aload_0); return stackItem[typecode]; } public String toString() { return isSuper ? "super" : "this"; } } /** An item representing a local variable. */ class LocalItem extends Item { /** The variable's register. */ int reg; /** The variable's type. */ Type type; LocalItem(Type type, int reg) { super(Code.typecode(type)); Assert.check(reg >= 0); this.type = type; this.reg = reg; } Item load() { if (reg <= 3) code.emitop0(iload_0 + Code.truncate(typecode) * 4 + reg); else code.emitop1w(iload + Code.truncate(typecode), reg); return stackItem[typecode]; } void store() { if (reg <= 3) code.emitop0(istore_0 + Code.truncate(typecode) * 4 + reg); else code.emitop1w(istore + Code.truncate(typecode), reg); code.setDefined(reg); } void incr(int x) { if (typecode == INTcode && x >= -32768 && x <= 32767) { code.emitop1w(iinc, reg, x); } else { load(); if (x >= 0) { makeImmediateItem(syms.intType, x).load(); code.emitop0(iadd); } else { makeImmediateItem(syms.intType, -x).load(); code.emitop0(isub); } makeStackItem(syms.intType).coerce(typecode); store(); } } public String toString() { return "localItem(type=" + type + "; reg=" + reg + ")"; } } /** An item representing a static variable or method. */ class StaticItem extends Item { /** The represented symbol. */ Symbol member; StaticItem(Symbol member) { super(Code.typecode(member.erasure(types))); this.member = member; } Item load() { code.emitop2(getstatic, pool.put(member)); return stackItem[typecode]; } void store() { code.emitop2(putstatic, pool.put(member)); } Item invoke() { MethodType mtype = (MethodType)member.erasure(types); int rescode = Code.typecode(mtype.restype); code.emitInvokestatic(pool.put(member), mtype); return stackItem[rescode]; } public String toString() { return "static(" + member + ")"; } } /** An item representing a dynamic call site. */ class DynamicItem extends StaticItem { DynamicItem(Symbol member) { super(member); } Item load() { assert false; return null; } void store() { assert false; } Item invoke() { // assert target.hasNativeInvokeDynamic(); MethodType mtype = (MethodType)member.erasure(types); int rescode = Code.typecode(mtype.restype); code.emitInvokedynamic(pool.put(member), mtype); return stackItem[rescode]; } public String toString() { return "dynamic(" + member + ")"; } } /** An item representing an instance variable or method. */ class MemberItem extends Item { /** The represented symbol. */ Symbol member; /** Flag that determines whether or not access is virtual. */ boolean nonvirtual; MemberItem(Symbol member, boolean nonvirtual) { super(Code.typecode(member.erasure(types))); this.member = member; this.nonvirtual = nonvirtual; } Item load() { code.emitop2(getfield, pool.put(member)); return stackItem[typecode]; } void store() { code.emitop2(putfield, pool.put(member)); } Item invoke() { MethodType mtype = (MethodType)member.externalType(types); int rescode = Code.typecode(mtype.restype); if ((member.owner.flags() & Flags.INTERFACE) != 0 && !nonvirtual) { code.emitInvokeinterface(pool.put(member), mtype); } else if (nonvirtual) { code.emitInvokespecial(pool.put(member), mtype); } else { code.emitInvokevirtual(pool.put(member), mtype); } return stackItem[rescode]; } void duplicate() { stackItem[OBJECTcode].duplicate(); } void drop() { stackItem[OBJECTcode].drop(); } void stash(int toscode) { stackItem[OBJECTcode].stash(toscode); } int width() { return 1; } public String toString() { return "member(" + member + (nonvirtual ? " nonvirtual)" : ")"); } } /** An item representing a literal. */ class ImmediateItem extends Item { /** The literal's value. */ Object value; ImmediateItem(Type type, Object value) { super(Code.typecode(type)); this.value = value; } private void ldc() { int idx = pool.put(value); if (typecode == LONGcode || typecode == DOUBLEcode) { code.emitop2(ldc2w, idx); } else { code.emitLdc(idx); } } Item load() { switch (typecode) { case INTcode: case BYTEcode: case SHORTcode: case CHARcode: int ival = ((Number)value).intValue(); if (-1 <= ival && ival <= 5) code.emitop0(iconst_0 + ival); else if (Byte.MIN_VALUE <= ival && ival <= Byte.MAX_VALUE) code.emitop1(bipush, ival); else if (Short.MIN_VALUE <= ival && ival <= Short.MAX_VALUE) code.emitop2(sipush, ival); else ldc(); break; case LONGcode: long lval = ((Number)value).longValue(); if (lval == 0 || lval == 1) code.emitop0(lconst_0 + (int)lval); else ldc(); break; case FLOATcode: float fval = ((Number)value).floatValue(); if (isPosZero(fval) || fval == 1.0 || fval == 2.0) code.emitop0(fconst_0 + (int)fval); else { ldc(); } break; case DOUBLEcode: double dval = ((Number)value).doubleValue(); if (isPosZero(dval) || dval == 1.0) code.emitop0(dconst_0 + (int)dval); else ldc(); break; case OBJECTcode: ldc(); break; default: Assert.error(); } return stackItem[typecode]; } //where /** Return true iff float number is positive 0. */ private boolean isPosZero(float x) { return x == 0.0f && 1.0f / x > 0.0f; } /** Return true iff double number is positive 0. */ private boolean isPosZero(double x) { return x == 0.0d && 1.0d / x > 0.0d; } CondItem mkCond() { int ival = ((Number)value).intValue(); return makeCondItem(ival != 0 ? goto_ : dontgoto); } Item coerce(int targetcode) { if (typecode == targetcode) { return this; } else { switch (targetcode) { case INTcode: if (Code.truncate(typecode) == INTcode) return this; else return new ImmediateItem( syms.intType, ((Number)value).intValue()); case LONGcode: return new ImmediateItem( syms.longType, ((Number)value).longValue()); case FLOATcode: return new ImmediateItem( syms.floatType, ((Number)value).floatValue()); case DOUBLEcode: return new ImmediateItem( syms.doubleType, ((Number)value).doubleValue()); case BYTEcode: return new ImmediateItem( syms.byteType, (int)(byte)((Number)value).intValue()); case CHARcode: return new ImmediateItem( syms.charType, (int)(char)((Number)value).intValue()); case SHORTcode: return new ImmediateItem( syms.shortType, (int)(short)((Number)value).intValue()); default: return super.coerce(targetcode); } } } public String toString() { return "immediate(" + value + ")"; } } /** An item representing an assignment expressions. */ class AssignItem extends Item { /** The item representing the assignment's left hand side. */ Item lhs; AssignItem(Item lhs) { super(lhs.typecode); this.lhs = lhs; } Item load() { lhs.stash(typecode); lhs.store(); return stackItem[typecode]; } void duplicate() { load().duplicate(); } void drop() { lhs.store(); } void stash(int toscode) { Assert.error(); } int width() { return lhs.width() + Code.width(typecode); } public String toString() { return "assign(lhs = " + lhs + ")"; } } /** An item representing a conditional or unconditional jump. */ class CondItem extends Item { /** A chain encomassing all jumps that can be taken * if the condition evaluates to true. */ Chain trueJumps; /** A chain encomassing all jumps that can be taken * if the condition evaluates to false. */ Chain falseJumps; /** The jump's opcode. */ int opcode; /* * An abstract syntax tree of this item. It is needed * for branch entries in 'CharacterRangeTable' attribute. */ JCTree tree; CondItem(int opcode, Chain truejumps, Chain falsejumps) { super(BYTEcode); this.opcode = opcode; this.trueJumps = truejumps; this.falseJumps = falsejumps; } Item load() { Chain trueChain = null; Chain falseChain = jumpFalse(); if (!isFalse()) { code.resolve(trueJumps); code.emitop0(iconst_1); trueChain = code.branch(goto_); } if (falseChain != null) { code.resolve(falseChain); code.emitop0(iconst_0); } code.resolve(trueChain); return stackItem[typecode]; } void duplicate() { load().duplicate(); } void drop() { load().drop(); } void stash(int toscode) { Assert.error(); } CondItem mkCond() { return this; } Chain jumpTrue() { if (tree == null) return Code.mergeChains(trueJumps, code.branch(opcode)); // we should proceed further in -Xjcov mode only int startpc = code.curCP(); Chain c = Code.mergeChains(trueJumps, code.branch(opcode)); code.crt.put(tree, CRTable.CRT_BRANCH_TRUE, startpc, code.curCP()); return c; } Chain jumpFalse() { if (tree == null) return Code.mergeChains(falseJumps, code.branch(Code.negate(opcode))); // we should proceed further in -Xjcov mode only int startpc = code.curCP(); Chain c = Code.mergeChains(falseJumps, code.branch(Code.negate(opcode))); code.crt.put(tree, CRTable.CRT_BRANCH_FALSE, startpc, code.curCP()); return c; } CondItem negate() { CondItem c = new CondItem(Code.negate(opcode), falseJumps, trueJumps); c.tree = tree; return c; } int width() { // a CondItem doesn't have a size on the stack per se. throw new AssertionError(); } boolean isTrue() { return falseJumps == null && opcode == goto_; } boolean isFalse() { return trueJumps == null && opcode == dontgoto; } public String toString() { return "cond(" + Code.mnem(opcode) + ")"; } } }