Skip to content

Commit

Permalink
JACOBIN-339 Initial code locating static initializers in classes
Browse files Browse the repository at this point in the history
  • Loading branch information
platypusguy committed Aug 20, 2023
1 parent c2a0a31 commit 4c0e273
Show file tree
Hide file tree
Showing 7 changed files with 78 additions and 37 deletions.
4 changes: 2 additions & 2 deletions src/classloader/cpParser.go
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ func parseConstantPool(rawBytes []byte, klass *ParsedClass) (int, error) {
return pos, cfe("Java module record requires Java 9 or later version")
}
nameIndex, _ := intFrom2Bytes(rawBytes, pos+1)
moduleName, err := fetchUTF8string(klass, nameIndex)
moduleName, err := FetchUTF8string(klass, nameIndex)
if err != nil {
break // error message will already have been shown
}
Expand All @@ -233,7 +233,7 @@ func parseConstantPool(rawBytes []byte, klass *ParsedClass) (int, error) {
return pos, cfe("Java package entry requires Java 9 or later version")
}
nameIndex, _ := intFrom2Bytes(rawBytes, pos+1)
packageName, err := fetchUTF8string(klass, nameIndex)
packageName, err := FetchUTF8string(klass, nameIndex)
if err != nil {
break // error message will already have been shown
}
Expand Down
10 changes: 5 additions & 5 deletions src/classloader/formatCheck.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ func formatCheckConstantPool(klass *ParsedClass) error {

nAndTentry := klass.nameAndTypes[nAndT.slot]
methodNameIndex := nAndTentry.nameIndex
name, err := fetchUTF8string(klass, methodNameIndex)
name, err := FetchUTF8string(klass, methodNameIndex)
if err != nil {
return cfe("Method Ref (at CP entry #" + strconv.Itoa(j) +
") has a Name and Type entry does not have a name that is a valid UTF8 entry")
Expand Down Expand Up @@ -274,14 +274,14 @@ func formatCheckConstantPool(klass *ParsedClass) error {
}

nAndTentry := klass.nameAndTypes[whichNandT]
_, err := fetchUTF8string(klass, nAndTentry.nameIndex)
_, err := FetchUTF8string(klass, nAndTentry.nameIndex)
if err != nil {
return cfe("Name and Type at CP entry #" + strconv.Itoa(j) +
" has a name index that points to an invalid UTF8 entry: " +
strconv.Itoa(nAndTentry.nameIndex))
}

desc, err2 := fetchUTF8string(klass, nAndTentry.descriptorIndex)
desc, err2 := FetchUTF8string(klass, nAndTentry.descriptorIndex)
if err2 != nil {
return cfe("Name and Type at CP entry #" + strconv.Itoa(j) +
" has a description index that points to an invalid UTF8 entry: " +
Expand Down Expand Up @@ -438,7 +438,7 @@ func formatCheckConstantPool(klass *ParsedClass) error {

natSlot := klass.cpIndex[nAndT].slot
nat := klass.nameAndTypes[natSlot] // gets the actual nameAndType entry
desc, err := fetchUTF8string(klass, nat.descriptorIndex)
desc, err := FetchUTF8string(klass, nat.descriptorIndex)
if err != nil {
return cfe("Descriptor in nameAndType entry of dynamic CP entry #" +
strconv.Itoa(j) + " is invalid: " + strconv.Itoa(nat.descriptorIndex))
Expand Down Expand Up @@ -489,7 +489,7 @@ func formatCheckConstantPool(klass *ParsedClass) error {

natSlot := klass.cpIndex[nAndTslot].slot
nat := klass.nameAndTypes[natSlot] // gets the actual nameAndType entry
desc, err := fetchUTF8string(klass, nat.descriptorIndex)
desc, err := FetchUTF8string(klass, nat.descriptorIndex)
if err != nil {
return cfe("Descriptor in nameAndType entry of dynamic CP entry #" +
strconv.Itoa(j) + " is invalid: " + strconv.Itoa(nat.descriptorIndex))
Expand Down
48 changes: 26 additions & 22 deletions src/classloader/methodParser.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,14 @@ import (
// as raw bytes. The description of the method entries in the spec is at:
// https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-4.html#jvms-4.6
// The layout of the entries is:
// method_info {
// u2 access_flags;
// u2 name_index;
// u2 descriptor_index;
// u2 attributes_count;
// attribute_info attributes[attributes_count];
// }
//
// method_info {
// u2 access_flags;
// u2 name_index;
// u2 descriptor_index;
// u2 attributes_count;
// attribute_info attributes[attributes_count];
// }
func parseMethods(bytes []byte, loc int, klass *ParsedClass) (int, error) {
pos := loc
var meth method
Expand Down Expand Up @@ -224,12 +225,14 @@ func parseCodeAttribute(att attr, meth *method, klass *ParsedClass) error {

// The Exceptions attribute of a method indicates which checked exceptions a method
// can throw. See: https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-4.html#jvms-4.7.5
// The structure of the Exceptions attribute of a method is: {
// u2 attribute_name_index;
// u4 attribute_length;
// u2 number_of_exceptions;
// u2 exception_index_table[number_of_exceptions];
// }
//
// The structure of the Exceptions attribute of a method is: {
// u2 attribute_name_index;
// u4 attribute_length;
// u2 number_of_exceptions;
// u2 exception_index_table[number_of_exceptions];
// }
//
// The last two entries are in attrContent, which is a []byte. The last entry, per the spec,
// is a ClassRef entry, which consists of a CP index that points to UTF8 entry containing the
// name of the checked exception class, e.g., java/io/IOException
Expand Down Expand Up @@ -258,7 +261,7 @@ func parseExceptionsMethodAttribute(attrib attr, meth *method, klass *ParsedClas
classRef := klass.classRefs[whichClassRef]

// the classRef should point to a UTF8 record with the name of the exception class
exceptionName, err2 := fetchUTF8string(klass, classRef)
exceptionName, err2 := FetchUTF8string(klass, classRef)
if err2 != nil {
return cfe("Exception attribute #" + strconv.Itoa(ex+1) +
" in method " + klass.utf8Refs[meth.name].content +
Expand All @@ -278,13 +281,14 @@ func parseExceptionsMethodAttribute(attrib attr, meth *method, klass *ParsedClas

// Per the spec, 'A MethodParameters attribute records information about the formal parameters
// of a method, such as their names.' See: https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-4.html#jvms-4.7.24
// u2 attribute_name_index;
// u4 attribute_length;
// u1 parameters_count;
// { u2 name_index;
// u2 access_flags;
// } parameters[parameters_count];
// }
//
// u2 attribute_name_index;
// u4 attribute_length;
// u1 parameters_count;
// { u2 name_index;
// u2 access_flags;
// } parameters[parameters_count];
// }
func parseMethodParametersAttribute(att attr, meth *method, klass *ParsedClass) error {
var err error
pos := 0
Expand All @@ -306,7 +310,7 @@ func parseMethodParametersAttribute(att attr, meth *method, klass *ParsedClass)
if paramNameIndex == 0 {
mpAttrib.name = ""
} else {
mpAttrib.name, err = fetchUTF8string(klass, paramNameIndex)
mpAttrib.name, err = FetchUTF8string(klass, paramNameIndex)
}
if err != nil {
return cfe("Error getting name of MethodParameters attribute #" +
Expand Down
6 changes: 3 additions & 3 deletions src/classloader/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ func parseClassName(bytes []byte, loc int, klass *ParsedClass) (int, error) {
// the entry pointed to by pointedToClassRef holds an index to
// a UTF-8 string that holds the class name
classNameIndex = klass.classRefs[pointedToClassRef.slot]
className, err := fetchUTF8string(klass, classNameIndex)
className, err := FetchUTF8string(klass, classNameIndex)
if err != nil {
return pos, errors.New("") // the error msg has already been show to user
}
Expand Down Expand Up @@ -315,7 +315,7 @@ func parseSuperClassName(bytes []byte, loc int, klass *ParsedClass) (int, error)
// a UTF-8 string that holds the class name
classNameIndex = klass.classRefs[pointedToClassRef.slot]

superClassName, err := fetchUTF8string(klass, classNameIndex)
superClassName, err := FetchUTF8string(klass, classNameIndex)
if err != nil {
return pos, errors.New("") // error has already been reported to user
}
Expand Down Expand Up @@ -375,7 +375,7 @@ func parseInterfaces(bytes []byte, loc int, klass *ParsedClass) (int, error) {
classEntry := klass.classRefs[classref.slot]

// use the class entry's index field to look up the UTF-8 string
interfaceName, err := fetchUTF8string(klass, classEntry)
interfaceName, err := FetchUTF8string(klass, classEntry)
if err != nil {
return pos, errors.New("") // error msg has already been shown
}
Expand Down
25 changes: 23 additions & 2 deletions src/classloader/parserUtils.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ func intFrom4Bytes(bytes []byte, pos int) (int, error) {

// finds and returns a UTF8 string when handed an index into the CP that points
// to a UTF8 entry. Does extensive checking of values.
func fetchUTF8string(klass *ParsedClass, index int) (string, error) {
func FetchUTF8string(klass *ParsedClass, index int) (string, error) {
if index < 1 || index > klass.cpCount-1 {
return "", cfe("attempt to fetch invalid UTF8 at CP entry #" + strconv.Itoa(index))
}
Expand All @@ -64,6 +64,27 @@ func fetchUTF8string(klass *ParsedClass, index int) (string, error) {
return klass.utf8Refs[i].content, nil
}

// finds and returns a UTF8 string when handed an index into the CP that points
// to a UTF8 entry in a loaded class. Similar to previous method, except that it
// works with a loaded class, rather than a parsed (i.e., pre-loaded) class.
// Does extensive checking of values.
func FetchUTF8stringInLoadedClass(klass *Klass, index int) (string, error) {
if index < 1 || index > len(klass.Data.CP.CpIndex)-1 {
return "", cfe("attempt to fetch invalid UTF8 at CP entry #" + strconv.Itoa(index))
}

if klass.Data.CP.CpIndex[index].Type != UTF8 {
return "", cfe("attempt to fetch UTF8 string from non-UTF8 CP entry #" + strconv.Itoa(index))
}

i := int(klass.Data.CP.CpIndex[index].Slot)
if i < 0 || i > len(klass.Data.CP.Utf8Refs)-1 {
return "", cfe("invalid index into UTF8 array of CP: " + strconv.Itoa(i))
}

return klass.Data.CP.Utf8Refs[i], nil
}

// like the preceding function, except this returns the slot number in the utf8Refs
// rather than the string that's in that slot.
func fetchUTF8slot(klass *ParsedClass, index int) (int, error) {
Expand Down Expand Up @@ -141,7 +162,7 @@ func resolveCPmethodRef(index int, klass *ParsedClass) (string, string, string,
methRef := klass.methodRefs[cpEnt.slot]
pointedToClassRef := klass.cpIndex[methRef.classIndex]
nameIndex := klass.classRefs[pointedToClassRef.slot]
className, err := fetchUTF8string(klass, nameIndex)
className, err := FetchUTF8string(klass, nameIndex)
if err != nil {
return "", "", "", cfe("ClassRef entry in MethodRef CP entry #" + strconv.Itoa(index) +
" does not point to a valid string")
Expand Down
6 changes: 3 additions & 3 deletions src/classloader/parserUtils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ func TestFetchValidUTF8string_Test0(t *testing.T) {
klass.utf8Refs = append(klass.utf8Refs, utf8Entry{"gherkin"})
klass.cpCount = 2

result, err := fetchUTF8string(&klass, 1)
result, err := FetchUTF8string(&klass, 1)
if err != nil {
t.Error("Unexpected error testing fetch of UTF8 entry")
}
Expand Down Expand Up @@ -215,7 +215,7 @@ func TestFetchInvalidUTF8string_Test1(t *testing.T) {
klass.utf8Refs = append(klass.utf8Refs, utf8Entry{"gherkin"})
klass.cpCount = 2

_, err := fetchUTF8string(&klass, 1)
_, err := FetchUTF8string(&klass, 1)
if err == nil {
t.Error("Expected error testing fetch of invalid UTF8 entry, but got none")
}
Expand Down Expand Up @@ -252,7 +252,7 @@ func TestFetchInvalidUTF8string_Test2(t *testing.T) {
klass := ParsedClass{}
klass.cpCount = 2

_, err := fetchUTF8string(&klass, 3) // index (3) can't be bigger than CP entries (2)
_, err := FetchUTF8string(&klass, 3) // index (3) can't be bigger than CP entries (2)
if err == nil {
t.Error("Expected error testing fetch of invalid UTF8 entry, but got none")
}
Expand Down
16 changes: 16 additions & 0 deletions src/jvm/instantiate.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,25 @@ func instantiateClass(classname string) (*object.Object, error) {
} // end of handling fields for one class or superclass
} // end of handling fields for classes with superclasses other than Object

// run intialization blocks
for i := 0; i < len(k.Data.Methods); i++ {
meth := k.Data.Methods[i]
nameIdx := meth.Name
methName, err := classloader.FetchUTF8stringInLoadedClass(k, int(nameIdx))
if err != nil {
continue // for the nonce skip the method if we can't find the name
} else if strings.HasPrefix(methName, "<clinit>") {
runInitializationBlock(k)
}
}
return &obj, nil
}

func runInitializationBlock(k *classloader.Klass) {
msg := fmt.Sprintf("***************** <clinit> found in %s **********", k.Data.Name)
fmt.Print(msg)
}

// creates a field for insertion into the object representation
func createField(f classloader.Field, k *classloader.Klass, classname string) (*object.Field, error) {
desc := k.Data.CP.Utf8Refs[f.Desc]
Expand Down

0 comments on commit 4c0e273

Please sign in to comment.