Homemade Java Virtual machines (iv)-objects, new, invokespecial
I. Representation of an Object
When I first started to learn Java, library various tutorials, the title is often "Java object-oriented advanced programming", usually the author will be compared with C + +, listed the advantages are often pure object-oriented, automatic garbage collection (do not manage memory), cross-platform (Write once, run Everywhere is the focus of the propaganda, provided that the JVM is installed on each platform, that there are no pointers (and later proved to be), security, etc. This article is to implement object-oriented (simple version, temporarily regardless of inheritance), the directives involved are:
New creates an object
GetField gets an instance property of an object (field), push to the operand stack
Putfield assigns a value to an object's properties by reference to the object and to the index of the constant pool Constant_fieldref_info type
Invokespecial calling constructors, instantiating methods
The storage object is primarily the instance property and type information that stores it, and it can be represented as an object in the following struct:
typedef struct _OBJECT {
Class* Pclass;
int length;
char* fields;
} Object;
typedef object* Reference;
Where the Pclass field is the class used to create the object, length is the number of properties for the object, fields is the field array pointer, and points to the starting address of the instance property array.
The code for creating an object can be as follows:
object* NewObject (openv *env, class* pclass) {
Object *obj;
int total_size;
Total_size = (pclass->fields_size+1) <<2;
obj = (object*) malloc (sizeof (Object) + total_size);
Obj->fields = (char*) (obj+1);
Obj->pclass = Pclass;
Obj->length = total_size;
Write a picture description here
Implementation of the new directive:
Opreturn do_new (openv *env)
{
Class* Pclass;
PRINTSD (To_short (ENV->PC));
Short index = To_short (ENV->PC);
Object *obj;
if (Env->current_class->this_class = = index) {
Pclass = env->current_class;
} else {
Todo:create object of Non-current-class
}
obj = NewObject (env, pclass);
Push_stackr (Env->current_stack, obj, Reference);
INC2_PC (ENV->PC);
Since the GetField and Putfield directives are manipulated by object references and constant pool indexes, if we manipulate the object's properties, we need to uniquely locate each property, and the current method is implemented by assigning a unique index to the instance field of each object. Let's say we already know the index of each property:
#define GET_FIELD_OFFSET (Index) (index) << 2)
#define Get_field (obj, Findex, ftype) * ((ftype*) (Obj->fields + get_field_offset (findex)))
#define Put_field (obj, Findex, Fvalue, Ftype) * ((ftype*) (Obj->fields + get_field_offset (findex))) =fvalue
With the object's reference (obj), the index of the property (Findex), the type of the property (Ftype), we can access the properties of an object (you need to know the value fvalue when saving).
In fact, the GetField and putfield instructions involve the stack of operands:
#define OP_GET_FIELDI (obj, Findex, ftype www.yigouyule2.cn/) push_stack (Env->current_stack, Get_field (obj, Findex, ftype), int)
#define OP_GET_FIELDF (obj, Findex, ftype Www.22yigouyule.cn/PUSH_STACK (Env->current_stack, Get_field (obj, Findex, ftype), float)
#define OP_GET_FIELDL (obj, Findex, ftype) Push_stackl (Env->current_stack, Get_field (obj, Findex, ftype), ftype)
#define OP_PUT_FIELDI (obj, Findex, www.huazongyule.com) Obj=pick_stackl (Env->current_stack, Reference); \
SP_DOWNL (env->current_stack); \
Put_field (obj, Findex, Pick_stacku (Env->current_stack, ftype), int)
#define OP_PUT_FIELDF (obj, Findex, ftype) Obj=pick_stackl (Env->current_stack, Reference); \
SP_DOWNL (env->current_stack); \
Put_field (obj, Findex, Pick_stacku (Env->current_stack, ftype), float)
#define OP_PUT_FIELDL (obj, Findex, ftype www.wansenpingtai22.cn) www.huaren88cai.cn Obj=pick_stackil ( www.mianyangbaojie.cn Env->current_stack, Reference);
Sp_downil (env->current_stack); \
The above macros can be used with ease in GetField, Putfield implementation functions.
Pick_stacku, Pick_stackl, Pick_stackil are defined as follows:
#define PICK_STACKU (Stack, VType) (* (vtype*) (stack->sp+sp_step))//Up to 4 bytes
#define PICK_STACKL (Stack, VType) (* (vtype*) (Stack->sp-sp_step_long))//down 8 bytes
#define Pick_stackil (Stack, VType www.liyigou99.cn) (* (vtype*) (Stack->sp-sp_step_ilong))//12 bytes down
Second, parsing instance properties
Consider the following procedure:
Point.java
Package test;
public class point{
private Double X;
private double y;
Public point (Double x, double y)
{
this.x = x;
This.y = y;
}
Private double distance (point P)
{
Double dx = p.x-this.x;
Double dy = p.y-this.y;
return dx*dx + dy *dy;
}
public static void Main (string[] args)
{
Point P1 = new Point (0,0);
Point P2 = new Point (3.0, 4.0);
Double dist = p1.distance (p2);
The byte code for the constructor point (double,double) is:
0:aload_0
1:invokespecial #1
4:aload_0
5:dload_1
6:putfield #2
9:aload_0
10:dload_3
11:putfield #3
14:return
1
where Putfield #2, 2 is the index of the constant pool, and the content is a structure of the Constant_fieldref_info type:
Constant Pool # #, #3的内容:
#2 Fieldref #4. #26//Test/point.x:d
#3 Fieldref #4. #27//Test/point.y:d
1
2
1
2
We need to parse the index of the constant pool to the specific field_info to uniquely determine the index of each field (property) in the object, and the reference relationships in the constant pool are as follows:
Write a picture description here
By visible, the fieldref corresponds to the Nameandtype's Name_index field, the Descriptor_index field is equal to the field_info corresponding field, and we find the actual field from the class's fields array.
The code for parsing instance properties is roughly as follows:
void Resolveclassinstancefield (class* caller_class, Constant_fieldref_info **pfield_ref)
{
Class* Callee_class;
Cp_info CALLEE_CP, CALLER_CP;
constant_fieldref_info* field_ref = *pfield_ref;
constant_nameandtype_info* Field_nt_info;
constant_utf8_info* Field_name_utf8, *tmp_field_name_utf8;
Constant_class_info *field_ref_class_info;
Field_info *field;
int I, found =0, Fields_count;
CALLER_CP = caller_class->constant_pool;
Field_ref_class_info = (constant_class_info*) (Caller_cp[field_ref->class_index]);
Callee_class = field_ref_class_info->pclass;
if (NULL = = Callee_class) {
printf ("NULL class"); exit (1);
}
Field_nt_info = (constant_nameandtype_info*) (Caller_cp[field_ref->name_and_type_index]);
CALLEE_CP = callee_class->constant_pool;
Fields_count = callee_class->fields_count;
for (i = 0; i < Fields_count; i++) {
field = (field_info*) (Callee_class->fields[i]);
if (not_acc_static (field->access_flags) &&
Field_nt_info->name_index = = Field->name_index &&
Field_nt_info->descriptor_index = = Field->descriptor_index) {
Field_ref->ftype = field->ftype; Type of instance property
Field_ref->findex = field->findex; Index of instance properties in an object
Found = 1;
Break
}
}
if (!found) {
Field_name_utf8 = (constant_utf8_info*) (Caller_cp[field_nt_info->name_index]);
printf ("error! Cannot resolve field:%s.%s ", field_name_utf8->bytes);
Exit (1);
}
Here we only consider the instance properties defined by the class itself (regardless of inherited properties), the type of the instance property, and the index in the object is determined in the fields of the parse class file:
void Parsefields (FILE *fp, Class *pclass)
{
...
ftype = * (char*) (Get_utf8 (Pclass->constant_pool[tmp_field->descriptor_index]));
Tmp_field->ftype = ftype;
if (not_acc_static (tmp_field->access_flags)) {//filter out static properties
Tmp_field->findex = Last_index;
if (ftype = = ' J ' | | ftype = = ' D ') {
last_index+=2; Long, double takes two units
} else {
Last_index+=1; Other data types account for one unit
}
}
...
Therefore, the implementation of the GetField directive can look like this:
Opreturn Do_getfield (openv *env)
{
Constant_fieldref_info *fieldref;
Cp_info CP;
Object *obj;
Short index = To_short (ENV->PC);
PRINTSD (To_short (ENV->PC));
CP = env->current_class->constant_pool;
FieldRef = (constant_fieldref_info*) (Cp[index]);
Get_stackr (Env->current_stack, obj, Reference);
if (0 = = Fieldref->ftype) {
Todo:resolve this field
Resolveclassinstancefield (Env->current_class, &fieldref);
}
Switch (fieldref->ftype) {
...
Case ' S '://Short
Op_get_fieldi (obj, Fieldref->findex, short);
Break
Case ' I '://Integer
Op_get_fieldi (obj, fieldref->findex, int);
Break
Case ' D '://Double
OP_GET_FIELDL (obj, Fieldref->findex, double);
Break
Default
printf ("Error:getfield, ftype=%d\n", Fieldref->ftype);
Exit (1);
Break
}
INC2_PC (ENV->PC);
Putfield directives can also be implemented similarly.
Third, analytic example method
or the byte code for the Point.java,main function above is:
0:new #4//Class Test/point
3:dup
4:dconst_0
5:dconst_0
6:invokespecial #5//Method "<init>":(DD) V
9:astore_1
10:new #4//Class Test/point
13:dup
14:ldc2_w #6//Double 3.0d
17:ldc2_w #8//Double 4.0d
20:invokespecial #5//Method "<init>":(DD) V
23:astore_2
24:aload_1
25:aload_2
26:invokespecial #10//Method Distance: (ltest/point;) D
29:dstore_3
30:return
One of the invokespecial #10就是调用Point的distance方法, #10是常量池中的索引, corresponds to the structure of a constant_methodref_info type, and the diagram in the constant pool is as follows:
Write a picture description here
This and fieldref can be said to be identical, through the MethodRef name_and_type_index field, find the corresponding nameandtype structure, and then traverse methods Array, Method_info name_ The index field, the Descriptor_index field, and the corresponding field in the Nameandtype are equal to the instance method that resolves to the object. (This method also applies to the parsing of constructors <init>)
Therefore, an instance of the invokespecial instruction can be as follows:
Opreturn do_invokespecial (openv *env)
{
PRINTSD (To_short (ENV->PC));
Short Mindex = To_short (ENV->PC);
INC2_PC (ENV->PC);
Callclassspecialmethod (env, mindex);
Callclassspecialmethod is roughly as follows:
void Callclassspecialmethod (openv* current_env, int mindex)
{
class* Current_class = current_env->current_class;
constant_methodref_info* method_ref = (constant_methodref_info*) (Current_class->constant_pool[mindex]);
if (method_ref->class_index! = Current_env->current_class->this_class) {
printf ("Skip Other Method"); Skipping instance methods of other classes
Return
}
if (NULL = = method_ref->ref_addr) {
This is similar to Resolveclassinstancefield.
Resolveclassspecialmethod (Current_class, &method_ref);
}
Call this method [see next section]
Callresolvedclassspecialmethod (current_env, method_ref);
Iv. Method of Invocation
In the above section, we parse out the instance method, and we'll call it next.
A method/function (called the called method) is called, and several things need to be done:
Create a new frame/stack frame
Copy parameters from the operand stack of the method caller (Invoker) to the new frame's local variable array (if there are parameters)
The execution context of the Save Method Caller (invoker) (instruction pointer position, current class, etc.)
Sets the execution context of the called method, pointing the instruction pointer to the first instruction of the called method
After the method call is complete, you need to:
Copies the return parameters to the operand stack of the method caller, if any
Restore the execution context before the call
Destroys the frame created when the method is called
The Callresolvedclassspecialmethod in the previous section can be implemented as follows:
void Callresolvedclassspecialmethod (openv* current_env, constant_methodref_info* method_ref)
{
Stackframe* STF, *last_stack;
constant_class_info* Class_info;
Method_info* method;
Code_attribute* code_attr;
int real_args_len = 0;
last_stack= current_env->current_stack;
1. Create new stack frame
method = (method_info*) (METHOD_REF->REF_ADDR);
Code_attr = (code_attribute*) (METHOD->CODE_ATTRIBUTE_ADDR);
STF = Newstackframe (Last_stack, code_attr);
2. Copy args
Real_args_len = Method->args_len + sz_ref;
LAST_STACK->SP-= Real_args_len;
memcpy (Stf->localvars, LAST_STACK->SP, Real_args_len);
3. Save Current environment
STF->LAST_PC = current_env->pc;
Stf->last_pc_end = current_env->pc_end;
Stf->last_pc_start = current_env->pc_start;
Stf->last_class = current_env->current_class;
4. Set new environment
Class_info = (constant_class_info*) (Current_env->current_class->constant_pool[method_ref->class_index]) ;
current_env->pc = Current_env->pc_start = code_attr->code;
Current_env->pc_end = Code_attr->code + code_attr->code_length;
Current_env->current_class = class_info->pclass;
Current_env->current_stack = STF;
When copying parameters, it is important to note that the instance method has an implicit parameter, which is the object reference to call the method, which is passed as the first parameter to the method and needs to be placed in the first position of the local variable array of the new frame.
Write a picture description here
After the call is finished (that is, a return series command is encountered), it is defined as a macro to call the return series instruction:
#define FUNC_RETURN (env) stackframe* STF = env->current_stack;\
Env->current_stack = stf->prev;\
ENV->PC = stf->last_pc;\
Env->pc_end = stf->last_pc_end;\
Env->pc_start = stf->last_pc_start;\
Env->current_class = stf->last_class;\
Free (STF); \
if (Env->current_stack = = NULL) {\
Exit (0); \
}
V. Testing
The debug results of compiling Point.java into point.class files, testing, Java source code and output are as follows:
Write a picture description here
Mentally calculate, compare debug output, know the result is correct. (distance calculation intentionally does not open square)
Vi. Summary
To summarize, in this chapter, we:
Ability to represent and create an object that implements the new directive
Implements the object property operation instruction GetField, Putfield
Ability to parse instance properties
Ability to parse instance methods
Implements the Invokespecial directive
In the test example, a two point object was created with a parametric constructor, one of the objects called the instance method distance, and the other object as a parameter, calculating the square of the distance, and running correctly.
Homemade Java Virtual machines (iv)-objects, new, invokespecial