All Downloads are FREE. Search and download functionalities are using the official Maven repository.

prompto.verifier.StackMapFrame Maven / Gradle / Ivy

The newest version!
package prompto.verifier;

import java.util.Arrays;

import prompto.compiler.MethodInfo;

public class StackMapFrame {

	static final byte FLAG_THIS_UNINIT = 0x01;

	int _offset;

	// See comment in StackMapTable about _frame_count about why these
	// fields are int32_t instead of u2.
	int _locals_size; // number of valid type elements in _locals
	int _stack_size; // number of valid type elements in _stack

	int _stack_mark; // Records the size of the stack prior to an
						// instruction modification, to allow rewinding
						// when/if an error occurs.

	int _max_locals;
	int _max_stack;

	byte _flags;
	VerificationType[] _locals; // local variable type array
	VerificationType[] _stack; // operand stack type array

	ClassVerifier _verifier; // the verifier verifying this method

	public StackMapFrame(int max_locals, int max_stack, ClassVerifier verifier) {
		this._offset = 0;
		this._locals_size = 0;
		this._stack_size = 0;
		this._stack_mark = 0;
		this._flags = 0;
		this._max_locals = max_locals;
		this._max_stack = max_stack;
		this._verifier = verifier;
		this._locals = new VerificationType[max_locals];
		Arrays.fill(_locals, VerificationType.bogus_type);
		this._stack = new VerificationType[max_stack];
		Arrays.fill(_stack, VerificationType.bogus_type);
	}

	public StackMapFrame(int offset, byte flags, int locals_size, int stack_size, int max_locals, int max_stack, VerificationType[] locals, VerificationType[] stack, ClassVerifier verifier) {
		this._offset = offset;
		this._flags = flags;
		this._locals_size = locals_size;
		this._stack_size = stack_size;
		this._stack_mark = -1;
		this._max_locals = max_locals;
		this._max_stack = max_stack;
		this._locals = locals;
		this._stack = stack;
		this._verifier = verifier;
	}

	public void set_locals_size(int locals_size) {
		_locals_size = locals_size;
	}

	public int locals_size() {
		return _locals_size;
	}

	private int max_locals() {
		return _max_locals;
	}

	public void set_stack_size(int stack_size) {
		_stack_size = _stack_mark = stack_size;
	}

	public int stack_size() {
		return _stack_size;
	}

	public VerificationType[] locals() {
		return _locals;
	}

	public VerificationType[] stack() {
		return _stack;
	}
	
	public void set_offset(int offset) {
		_offset = offset;
	}

	public int offset() {
		return _offset;
	}

	public void set_flags(byte flags) {
		_flags = flags;
	}

	public byte flags() {
		return _flags;
	}

	boolean flag_this_uninit() { 
		return (_flags & FLAG_THIS_UNINIT) != 0; 
	}

	private ClassVerifier verifier() {
		return _verifier;
	}

	public void set_mark() {
		// Put bogus type to indicate it's no longer valid.
		if (_stack_mark != -1) {
			for (int i = _stack_mark - 1; i >= _stack_size; --i) {
				_stack[i] = VerificationType.bogus_type;
			}
		}
		_stack_mark = _stack_size;
	}

	public VerificationType set_locals_from_arg(MethodInfo m, VerificationType thisKlass) {
		SignatureStream ss = new SignatureStream(m.getSignature().getValue());
		int init_local_num = 0;
		if (!m.isStatic()) {
			init_local_num++;
			// add one extra argument for instance method
			if ("".equals(m.getName().toString()) && !"java/lang/Object".equals(thisKlass.name())) {
				_locals[0] = VerificationType.uninitialized_this_type;
				_flags |= FLAG_THIS_UNINIT;
			} else {
				_locals[0] = thisKlass;
			}
		}

		// local num may be greater than size of parameters because long/double
		// occupies two slots
		while (!ss.at_return_type()) {
			init_local_num += _verifier.change_sig_to_verificationType(ss, _locals, init_local_num);
			ss.next();
		}
		_locals_size = init_local_num;

		switch (ss.type()) {
		case T_OBJECT:
		case T_ARRAY:
			String sym = ss.as_symbol();
			return VerificationType.reference_type(sym);
		case T_INT:
			return VerificationType.integer_type;
		case T_BYTE:
			return VerificationType.byte_type;
		case T_CHAR:
			return VerificationType.char_type;
		case T_SHORT:
			return VerificationType.short_type;
		case T_BOOLEAN:
			return VerificationType.boolean_type;
		case T_FLOAT:
			return VerificationType.float_type;
		case T_DOUBLE:
			return VerificationType.double_type;
		case T_LONG:
			return VerificationType.long_type;
		case T_VOID:
			return VerificationType.bogus_type;
		default:
			throw new UnsupportedOperationException();
		}
	}

	public void push_stack(VerificationType type) {
		if (type.is_check())
			throw new VerifierException("Must be a real type");
		if (_stack_size >= _max_stack)
			throw new VerifierException("Operand stack overflow");
		_stack[_stack_size++] = type;
	}

	public void push_stack_2(VerificationType type1, VerificationType type2) {
		if (!type1.is_long() && !type1.is_double())
			throw new VerifierException("must be long/double");
		if (!type2.is_long2() && !type2.is_double2())
			throw new VerifierException("must be long/double_2");
		if (_stack_size >= _max_stack - 1) {
			throw new VerifierException("Operand stack overflow");
		}
		_stack[_stack_size++] = type1;
		_stack[_stack_size++] = type2;
	}

	public void copy_locals(StackMapFrame src) {
	  int len = src.locals_size() < _locals_size ? src.locals_size() : _locals_size;
	  for (int i = 0; i < len; i++) {
	    _locals[i] = src.locals()[i];
	  }	
	}

	public VerificationType pop_stack(VerificationType type) {
		if (_stack_size != 0) {
			VerificationType top = _stack[_stack_size - 1];
			boolean subtype = type.is_assignable_from(top, false, verifier());
			if (subtype) {
				--_stack_size;
				return top;
			}
		}
		return pop_stack_ex(type);
	}

	private VerificationType pop_stack_ex(VerificationType type) {
		if (_stack_size <= 0) {
			throw new VerifierException("Operand stack underflow");
		}
		VerificationType top = _stack[--_stack_size];
		boolean subtype = type.is_assignable_from(top, false, verifier());
		if (!subtype) {
			throw new VerifierException("Bad type on operand stack");
		}
		return top;
	}

	public VerificationType pop_stack() {
		throw new UnsupportedOperationException();
	}

	public void set_local(short index, VerificationType type) {
		if (type.is_check())
			throw new VerifierException("Must be a real type");
		if (index >= _max_locals) {
			throw new VerifierException("Local variable table overflow");
		}
		// If type at index is double or long, set the next location to be
		// unusable
		if (_locals[index].is_double() || _locals[index].is_long()) {
			if ((index + 1) >= _locals_size)
				throw new VerifierException("Local variable table overflow");
			_locals[index + 1] = VerificationType.bogus_type;
		}
		// If type at index is double_2 or long_2, set the previous location to
		// be unusable
		if (_locals[index].is_double2() || _locals[index].is_long2()) {
			if (index < 1)
				throw new VerifierException("Local variable table underflow");
			_locals[index - 1] = VerificationType.bogus_type;
		}
		_locals[index] = type;
		if (index >= _locals_size) {
			for (int i = _locals_size; i < index; i++) {
				if (_locals[i] != VerificationType.bogus_type)
					throw new VerifierException("holes must be bogus type");
			}
			_locals_size = index + 1;
		}
	}

	public VerificationType get_local(short index, VerificationType type) {
		if (index >= _max_locals) {
			throw new VerifierException("Local variable table overflow");
		}
		boolean subtype = type.is_assignable_from(_locals[index], false, verifier());
		if (!subtype) {
			throw new VerifierException("Bad local variable type");
		}
		if (index >= _locals_size) {
			_locals_size = index + 1;
		}
		return _locals[index];
	}

	public void initialize_object(VerificationType old_object, VerificationType new_object) {
		for (int i = 0; i < _max_locals; i++) {
			if (_locals[i].equals(old_object)) {
				_locals[i] = new_object;
			}
		}
		for (int i = 0; i < _stack_size; i++) {
			if (_stack[i].equals(old_object)) {
				_stack[i] = new_object;
			}
		}
		if (old_object == VerificationType.uninitialized_this_type) {
			// "this" has been initialized - reset flags
			_flags = 0;
		}
	}

	public void reset() {
		for (int i = 0; i < _max_locals; i++) {
			_locals[i] = VerificationType.bogus_type;
		}
		for (int i = 0; i < _max_stack; i++) {
			_stack[i] = VerificationType.bogus_type;
		}
	}

	public void copy_stack(StackMapFrame src) {
		int len = src.stack_size() < _stack_size ? src.stack_size() : _stack_size;
		  for (int i = 0; i < len; i++) {
		    _stack[i] = src._stack[i];
		  }
	}

	public boolean is_assignable_to(StackMapFrame target, boolean is_exception_handler) {
	  if (_max_locals != target.max_locals()) {
		  throw new VerifierException("Locals size mismatch");
	  }
	  if (_stack_size != target.stack_size()) {
		  throw new VerifierException("Stack size mismatch");
	  }
	  // Only need to compare type elements up to target->locals() or target->stack().
	  // The remaining type elements in this state can be ignored because they are
	  // assignable to bogus type.
	  int mismatch_loc = is_assignable_to(_locals, target.locals(), target.locals_size());
	  if (mismatch_loc != target.locals_size()) {
		  throw new VerifierException("bad type");
	  }
	  mismatch_loc = is_assignable_to(_stack, target.stack(), _stack_size);
	  if (mismatch_loc != _stack_size) {
		  throw new VerifierException("bad type");
	  }

	  boolean match_flags = (_flags | target.flags()) == target.flags();
	  if (match_flags || is_exception_handler && has_flag_match_exception(target)) {
	    return true;
	  } else {
		  throw new VerifierException("bad flags");
	  }
	}

	// Returns the location of the first mismatch, or 'len' if there are no mismatches
	int is_assignable_to(VerificationType[] from, VerificationType[] to, int len) {
	  int i = 0;
	  for (i = 0; i < len; i++) {
	    if (!to[i].is_assignable_from(from[i], false, verifier())) {
	      break;
	    }
	  }
	  return i;
	}

	boolean has_flag_match_exception(StackMapFrame target) {
	  // We allow flags of {UninitThis} to assign to {} if-and-only-if the
	  // target frame does not depend upon the current type.
	  // This is slightly too strict, as we need only enforce that the
	  // slots that were initialized by the  (the things that were
	  // UninitializedThis before initialize_object() converted them) are unused.
	  // However we didn't save that information so we'll enforce this upon
	  // anything that might have been initialized.  This is a rare situation
	  // and javac never generates code that would end up here, but some profilers
	  // (such as NetBeans) might, when adding exception handlers in 
	  // methods to cover the invokespecial instruction.  See 7020118.

	  if(max_locals() != target.max_locals() || stack_size() != target.stack_size())
		  throw new VerifierException("StackMap sizes must match");

	  VerificationType top = VerificationType.top_type;
	  VerificationType this_type = verifier().current_type();

	  if (!flag_this_uninit() || target.flags() != 0) {
	    return false;
	  }

	  for (int i = 0; i < target.locals_size(); ++i) {
	    if (locals()[i] == this_type && target.locals()[i] != top) {
	      return false;
	    }
	  }

	  for (int i = 0; i < target.stack_size(); ++i) {
	    if (stack()[i] == this_type && target.stack()[i] != top) {
	      return false;
	    }
	  }

	  return true;
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy