/*
 *   
 *
 * Copyright  1990-2006 Sun Microsystems, Inc. All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
 * 
 * This program 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. 
 * 
 * This program 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 at /legal/license.txt). 
 * 
 * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa
 * Clara, CA 95054 or visit www.sun.com if you need additional
 * information or have any questions. 
 */

package components;
import java.io.DataOutput;
import java.io.DataInput;
import java.io.IOException;
import util.DataFormatException;
import jcc.Const;

public class StackMapFrame { 
    // A single frame in the stack map table

    public static final int ITEM_Bogus = 0;
    public static final int ITEM_Integer = 1;
    public static final int ITEM_Float = 2;
    public static final int ITEM_Double = 3;
    public static final int ITEM_Long = 4;
    public static final int ITEM_Null = 5;
    public static final int ITEM_InitObject = 6;
    public static final int ITEM_Object = 7;
    public static final int ITEM_NewObject = 8;

    int offset;
    ConstantObject locals[];
    ConstantObject stack[];

    // Read in a frame from the specified data input stream.
    StackMapFrame(DataInput in, ConstantObject globals[]) 
	             throws IOException { 
	   this.offset = in.readShort();		    
	   this.locals = readOneTable(in, globals);
	   this.stack = readOneTable(in, globals);
    }

    private ConstantObject[] 
    readOneTable(DataInput in, ConstantObject globals[]) 
		  throws IOException { 
	int length = in.readShort();
	ConstantObject result[] = new ConstantObject[length];
	for (int i = 0; i < length; i++) { 
	    int tag = in.readByte();
	    switch(tag) { 
	    case ITEM_Bogus:
	    case ITEM_Integer: case ITEM_Float:
	    case ITEM_Double:  case ITEM_Long:
	    case ITEM_Null:    case ITEM_InitObject:
		// Small non-negative values represent the corresponding
		// type.
		result[i] = new SingleValueConstant(tag);
		break;

	    case ITEM_Object:
		result[i] = globals[in.readShort()];
		break;

	    case ITEM_NewObject:
		// It's not really worth creating a new
		// type to represent this.  Negative numbers represent new
		// objects.
		result[i] = new SingleValueConstant(~in.readShort());
		break;

	    default:
		System.out.println("Unknown tag");
	    }
	}
	return result;
    }

    public int writeData(DataOutput out) throws IOException {
	out.writeShort(offset);
	// Return the number of bytes that we've written
	return 2 + writeData(out, locals) + writeData(out, stack);
    }

    public int writeData(DataOutput out, ConstantObject[] table) 
	   throws IOException 
    { 
	int count = 0;
	out.writeShort(table.length);
	count += 2;
	for (int i = 0; i < table.length; i++) { 
	    if (table[i] instanceof SingleValueConstant) { 
		int value = ((SingleValueConstant)table[i]).value;
		if (value < 0) { 
		    out.writeByte(ITEM_NewObject);
		    out.writeShort(~value);
		    count += 3;
		} else { 
		    out.writeShort(value);	    
		    count += 2;
		}
	    } else if (table[i] instanceof ClassConstant) { 
		out.writeByte(ITEM_Object);
		out.writeShort(((ClassConstant)table[i]).name.index);
		count += 3;
	    }
	}
	return count;
    }

    public void
    externalize( ConstantPool p ){
	// do nothing
    }

    public void countConstantReferences(boolean isRelocatable) { 
	if (isRelocatable) { 
	    // Is this the right thing??
	    for (int i = 0; i < locals.length; i++) { 
		locals[i].incReference();
	    }
	    for (int i = 0; i < stack.length; i++) { 
		stack[i].incReference();
	    }
	}
    }
    
    // Return a bitmap indicating which values on the stack are pointers
    public boolean[] getStackBitmap() { 
	return getBitmap(stack);
    }

    public int getStackSize() { 
        ConstantObject table[] = stack;
        int extra = 0;
	for (int i = 0; i < table.length; i++) { 
	    if (table[i] instanceof SingleValueConstant) { 
		int value = ((SingleValueConstant)table[i]).value;
		if (value == ITEM_Long || value == ITEM_Double) { 
		    extra++;
		}
	    }
	} 
	return table.length + extra;
    }

    // Return a bitmap indicating which values on the locals are pointers
    public boolean[] getLocalsBitmap() {
	return getBitmap(locals);
    }

    private boolean[] getBitmap(ConstantObject table[]) { 
	int extra = 0;
	// Count the actual number of values, since doubles and longs take up 
	// two slots
	for (int i = 0; i < table.length; i++) { 
	    if (table[i] instanceof SingleValueConstant) { 
		int value = ((SingleValueConstant)table[i]).value;
		if (value == ITEM_Long || value == ITEM_Double) { 
		    extra++;
		}
	    }
	} 
	boolean result[] = new boolean[table.length + extra];
	extra = 0;
	// Fill in the table
	for (int i = 0; i < table.length; i++) { 
	    if (table[i] instanceof SingleValueConstant) { 
		int value = ((SingleValueConstant)table[i]).value;
		if (value == ITEM_Long || value == ITEM_Double) { 
		    extra++;
		} else if (value < 0 || value == ITEM_InitObject) { 
		    result[i + extra] = true;
		}
	    } else if (table[i] instanceof ClassConstant) { 
		result[i + extra] = true;
	    } else { 
		throw new RuntimeException("Unknown table type");
	    }
	}
	return result;
    }

    public int getOffset() { 
	return offset;
    }
}
