From 4c0e273eb42e39ed37be2ce397429a8f270cbca7 Mon Sep 17 00:00:00 2001 From: Andrew Binstock <920630+platypusguy@users.noreply.github.com> Date: Sat, 19 Aug 2023 22:07:58 -0700 Subject: [PATCH] JACOBIN-339 Initial code locating static initializers in classes --- src/classloader/cpParser.go | 4 +-- src/classloader/formatCheck.go | 10 +++--- src/classloader/methodParser.go | 48 ++++++++++++++++------------- src/classloader/parser.go | 6 ++-- src/classloader/parserUtils.go | 25 +++++++++++++-- src/classloader/parserUtils_test.go | 6 ++-- src/jvm/instantiate.go | 16 ++++++++++ 7 files changed, 78 insertions(+), 37 deletions(-) diff --git a/src/classloader/cpParser.go b/src/classloader/cpParser.go index 6618b7c8..f073bf70 100644 --- a/src/classloader/cpParser.go +++ b/src/classloader/cpParser.go @@ -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 } @@ -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 } diff --git a/src/classloader/formatCheck.go b/src/classloader/formatCheck.go index 2ce847b3..e9cb678a 100644 --- a/src/classloader/formatCheck.go +++ b/src/classloader/formatCheck.go @@ -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") @@ -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: " + @@ -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)) @@ -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)) diff --git a/src/classloader/methodParser.go b/src/classloader/methodParser.go index f6959030..65bb4c07 100644 --- a/src/classloader/methodParser.go +++ b/src/classloader/methodParser.go @@ -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 @@ -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 @@ -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 + @@ -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 @@ -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 #" + diff --git a/src/classloader/parser.go b/src/classloader/parser.go index bee27766..fb1b3fd6 100644 --- a/src/classloader/parser.go +++ b/src/classloader/parser.go @@ -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 } @@ -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 } @@ -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 } diff --git a/src/classloader/parserUtils.go b/src/classloader/parserUtils.go index 828bef76..0a802213 100644 --- a/src/classloader/parserUtils.go +++ b/src/classloader/parserUtils.go @@ -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)) } @@ -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) { @@ -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") diff --git a/src/classloader/parserUtils_test.go b/src/classloader/parserUtils_test.go index 063f338e..24082c66 100644 --- a/src/classloader/parserUtils_test.go +++ b/src/classloader/parserUtils_test.go @@ -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") } @@ -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") } @@ -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") } diff --git a/src/jvm/instantiate.go b/src/jvm/instantiate.go index f887c62b..729e5caa 100644 --- a/src/jvm/instantiate.go +++ b/src/jvm/instantiate.go @@ -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, "") { + runInitializationBlock(k) + } + } return &obj, nil } +func runInitializationBlock(k *classloader.Klass) { + msg := fmt.Sprintf("***************** 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]