自己动手写JVM——(6)类与对象机制 操作实例数据

类与对象

public class MyObject {
public static int staticVar;
public int instanceVar;

public static void main(String[] args) {
    int x = 32768;                       // ldc
    MyObject myObj = new MyObject();     // new
    MyObject.staticVar = x;              // putstatic
    x = MyObject.staticVar;              // getstatic
    myObj.instanceVar = x;               // putfield
    x = myObj.instanceVar;               // getfield
    Object obj = myObj;
    if (obj instanceof MyObject) {       // instanceof
        myObj = (MyObject) obj;          // checkcast
        System.out.println(myObj.instanceVar);
    }
}

以上面这段为例,我们继续说一下JVM对类与对象的实现机制

PUTFIELD

    myObj.instanceVar = x;               // putfield
type PUT_FIELD struct {
	base.Index16Instruction
}

func (self *PUT_FIELD) Execute(frame *rtda.Frame) {
	currentMethod := frame.Method()
	currentClass := currentMethod.Class()
	cp := currentClass.ConstantPool()
	fieldRef := cp.GetConstant(self.Index).(*heap.FieldRef)
	field := fieldRef.ResolvedField()

	if field.IsStatic() {
		panic("java.lang.IncompatibleClassChangeError")
	}
	if field.IsFinal() {
		if currentClass != field.Class() || currentMethod.Name() != "<init>" {
			panic("java.lang.IllegalAccessError")
		}
	}

	descriptor := field.Descriptor()
	slotId := field.SlotId()
	stack := frame.OperandStack()

	switch descriptor[0] {
	case 'Z', 'B', 'C', 'S', 'I':
		val := stack.PopInt()
		ref := stack.PopRef()
		if ref == nil {
			panic("java.lang.NullPointerException")
		}
		ref.Fields().SetInt(slotId, val)
    // ...
	default:
		// TODO
	}
}

其实可以发现,PUTFIELD的实现与PUTSTATIC很相似,下面的代码同样是从当前函数栈帧中获取当前函数的类,再找到对应的fieldRef并解引用,获取fieldRef对应的Field,检查其Static和Final并进行对应的处理。

	currentMethod := frame.Method()
	currentClass := currentMethod.Class()
	cp := currentClass.ConstantPool()
	fieldRef := cp.GetConstant(self.Index).(*heap.FieldRef)
	field := fieldRef.ResolvedField()

	if field.IsStatic() {
		panic("java.lang.IncompatibleClassChangeError")
	}
	if field.IsFinal() {
		if currentClass != field.Class() || currentMethod.Name() != "<init>" {
			panic("java.lang.IllegalAccessError")
		}
	}

不同点在于下面:这次我们不是直接去修改Class的StaticVars,而是去修改了当前实例的对应的内存

	descriptor := field.Descriptor()
	slotId := field.SlotId()
	stack := frame.OperandStack()

	switch descriptor[0] {
	case 'Z', 'B', 'C', 'S', 'I':
		val := stack.PopInt()
		ref := stack.PopRef()
		if ref == nil {
			panic("java.lang.NullPointerException")
		}
		ref.Fields().SetInt(slotId, val)
    // ...
	default:
		// TODO
	}

我们分开来说:

		val := stack.PopInt()
		ref := stack.PopRef()

val是我们将要赋予的值,而ref是一个Object,是当前类实例的引用,没错就是this指针,让我们来回顾一下Object的结构

type Object struct {
	class *Class
	data interface{}
	extra interface{} // 记录Object结构体实例的额外信息
}

func newObject(class *Class) *Object {
	return &Object{
		class: class,
		data: newSlots(class.instanceSlotCount), // 分配类实例内存
	}
}

Object.data是与Class.instanceSlotCount占用量相同的数据区。

func (self *Object) Fields() Slots {
	return self.data.(Slots)
}

func (self Slots) SetInt(index uint, val int32) {
	self[index].num = val
}

ref.Fields().SetInt()就是在Object的对应数据区设置val。

GETFIELD

    x = myObj.instanceVar;               // getfield
type GET_FIELD struct {
	base.Index16Instruction
}

func (self *GET_FIELD) Execute(frame *rtda.Frame) {
	cp := frame.Method().Class().ConstantPool()
	fieldRef := cp.GetConstant(self.Index).(*heap.FieldRef)
	field := fieldRef.ResolvedField()

	if field.IsStatic() {
		panic("java.lang.IncompatibleClassChangeError")
	}

	stack := frame.OperandStack()
	ref := stack.PopRef() // this指针
	if ref == nil {
		panic("java.lang.NullPointerException")
	}

	descriptor := field.Descriptor()
	slotId := field.SlotId()
	slots := ref.Fields() // 当前实例所有的字段

	switch descriptor[0] {
    // ...
	case 'L', '[':
		stack.PushRef(slots.GetRef(slotId)) // 从指定的字段中获取数据 并压入操作数栈
	default:
		// TODO
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值