Skip to content

Commit

Permalink
JACOBIN-563 Fixed minor repercussions of having ThrowExNil() return a…
Browse files Browse the repository at this point in the history
… bool
  • Loading branch information
platypusguy committed Aug 11, 2024
1 parent 76381c8 commit 0ef4103
Show file tree
Hide file tree
Showing 3 changed files with 169 additions and 63 deletions.
5 changes: 3 additions & 2 deletions src/globals/globals.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ type Globals struct {
// Get around the golang circular dependency. To be set up in jvmStart.go
// Enables gfunctions to call these functions through a global variable.
FuncInstantiateClass func(string, *list.List) (any, error)
FuncThrowException func(int, string)
FuncThrowException func(int, string) bool
FuncFillInStackTrace func([]any) any
}

Expand Down Expand Up @@ -290,9 +290,10 @@ func fakeInstantiateClass(classname string, frameStack *list.List) (any, error)
}

// Fake ThrowEx() in exceptions.go
func fakeThrowEx(whichEx int, msg string) {
func fakeThrowEx(whichEx int, msg string) bool {
errMsg := fmt.Sprintf("\n*Attempt to access uninitialized ThrowEx pointer func")
fmt.Fprintf(os.Stderr, errMsg)
return false
}

func InitStringPool() {
Expand Down
165 changes: 104 additions & 61 deletions src/jvm/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -2899,18 +2899,21 @@ frameInterpreter:
// and doesn't change the stack if the cast is legal.
// Because this uses the same logic as INSTANCEOF, any change here should
// be made to INSTANCEOF

ref := peek(f) // peek b/c the objectRef is *not* removed from the op stack
if ref == nil { // if ref is nil, just carry on
f.PC += 2 // move past two bytes pointing to comp object
goto checkcastOK
f.PC += 1
continue // cannot goto checkcastOK, b/c golang doesn't allow a jump over variable initialization
}

var obj *object.Object
switch ref.(type) {
case *object.Object:
if object.IsNull(ref) { // if ref is null, just carry on
f.PC += 2 // move past two bytes pointing to comp object
goto checkcastOK
f.PC += 1
continue
} else {
obj = (ref).(*object.Object)
}
Expand All @@ -2929,10 +2932,49 @@ frameInterpreter:
f.PC += 2
CP := f.CP.(*classloader.CPool)
CPentry := CP.CpIndex[CPslot]
if CPentry.Type == classloader.ClassRef || CPentry.Type == classloader.Interface {
classNamePtr := classloader.FetchCPentry(CP, CPslot)

var targetClassType = types.Error
if CPentry.Type == classloader.Interface {
targetClassType = types.Interface
} else if strings.HasPrefix(*(classNamePtr.StringVal), "[") {
targetClassType = types.Array
} else {
targetClassType = types.NonArrayObject
}

var checkcastStatus bool
switch targetClassType {
case types.NonArrayObject:
checkcastStatus = checkcastNonArrayObject(obj, *(classNamePtr.StringVal))
case types.Array:
checkcastStatus = checkcastArray(obj, *(classNamePtr.StringVal))
case types.Interface:
checkcastStatus = checkcastInterface(obj, *(classNamePtr.StringVal))
default:
errMsg := fmt.Sprintf("CHECKCAST: expected to verify class or interface, but got none")
status := exceptions.ThrowEx(excNames.InvalidTypeException, errMsg, f)
if status != exceptions.Caught {
return errors.New(errMsg) // applies only if in test
}
}

if checkcastStatus == false {
glob.ErrorGoStack = string(debug.Stack())
errMsg := fmt.Sprintf("CHECKCAST: %s is not castable with respect to %s",
*(stringPool.GetStringPointer(obj.KlassName)), *(classNamePtr.StringVal))
status := exceptions.ThrowEx(excNames.ClassCastException, errMsg, f)
if status != exceptions.Caught {
return errors.New(errMsg) // applies only if in test
}
}

// if it is castable, do nothing.

if CPentry.Type == classloader.ClassRef {
// slot of ClassRef points to a CP entry for a UTF8 record w/ name of class
var className string
classNamePtr := classloader.FetchCPentry(CP, CPslot)
classNamePtr = classloader.FetchCPentry(CP, CPslot)
if classNamePtr.RetType != classloader.IS_STRING_ADDR {
glob.ErrorGoStack = string(debug.Stack())
errMsg := fmt.Sprintf("CHECKCAST: Invalid classRef found, classNamePtr.RetType=%d", classNamePtr.RetType)
Expand Down Expand Up @@ -2983,63 +3025,64 @@ frameInterpreter:
> TC and SC are reference types, and type SC can be cast to TC by
recursive application of these rules. */

if strings.HasPrefix(className, "[") { // the object being checked is an array
if obj.KlassName != types.InvalidStringIndex {
sptr := stringPool.GetStringPointer(obj.KlassName)
// for the nonce if they're both the same type of arrays, we're good
// TODO: if both are arrays of reference, check the leaf types
if *sptr == className || strings.HasPrefix(className, *sptr) {
break // exit this bytecode processing
} else {
/*** TODO: bypass this Throw action. Right thing to do?
errMsg := fmt.Sprintf("CHECKCAST: %s is not castable with respect to %s", className, *sptr)
status := exceptions.ThrowEx(exceptions.ClassCastException, errMsg)
if status != exceptions.Caught {
return errors.New(errMsg) // applies only if in test
}
***/
warnMsg := fmt.Sprintf("CHECKCAST: casting %s to %s might be unpleasant!", className, *sptr)
_ = log.Log(warnMsg, log.WARNING)
}
} else {
glob.ErrorGoStack = string(debug.Stack())
errMsg := fmt.Sprintf("CHECKCAST: Klass field for object is nil")
status := exceptions.ThrowEx(excNames.ClassCastException, errMsg, f)
if status != exceptions.Caught {
return errors.New(errMsg) // applies only if in test
}
}
} else { // the object being checked is a class
classPtr := classloader.MethAreaFetch(className)
if classPtr == nil { // class wasn't loaded, so load it now
if classloader.LoadClassFromNameOnly(className) != nil {
glob.ErrorGoStack = string(debug.Stack())
return errors.New("CHECKCAST: Could not load class: " + className)
}
classPtr = classloader.MethAreaFetch(className)
}

// if classPtr does not point to the entry for the same class, then examine superclasses
if classPtr != classloader.MethAreaFetch(*(stringPool.GetStringPointer(obj.KlassName))) {
if isClassAaSublclassOfB(obj.KlassName, stringPool.GetStringIndex(&className)) {
goto checkcastOK
}

glob.ErrorGoStack = string(debug.Stack())
errMsg := fmt.Sprintf("CHECKCAST: %s is not castable with respect to %s",
className, classPtr.Data.Name)
status := exceptions.ThrowEx(excNames.ClassCastException, errMsg, f)
if status != exceptions.Caught {
return errors.New(errMsg) // applies only if in test
}
} else {
goto checkcastOK // they both point to the same class, so perforce castable
}
} // end of checking an object that's not an array
}
checkcastOK:
f.PC += 1
continue // if CHECKCAST succeeds, do nothing
// if strings.HasPrefix(className, "[") { // the object being checked is an array
// if obj.KlassName != types.InvalidStringIndex {
// sptr := stringPool.GetStringPointer(obj.KlassName)
// // for the nonce if they're both the same type of arrays, we're good
// // TODO: if both are arrays of reference, check the leaf types
// if *sptr == className || strings.HasPrefix(className, *sptr) {
// break // exit this bytecode processing
// } else {
// /*** TODO: bypass this Throw action. Right thing to do?
// errMsg := fmt.Sprintf("CHECKCAST: %s is not castable with respect to %s", className, *sptr)
// status := exceptions.ThrowEx(exceptions.ClassCastException, errMsg)
// if status != exceptions.Caught {
// return errors.New(errMsg) // applies only if in test
// }
// ***/
// warnMsg := fmt.Sprintf("CHECKCAST: casting %s to %s might be unpleasant!", className, *sptr)
// _ = log.Log(warnMsg, log.WARNING)
// }
// } else {
// glob.ErrorGoStack = string(debug.Stack())
// errMsg := fmt.Sprintf("CHECKCAST: Klass field for object is nil")
// status := exceptions.ThrowEx(excNames.ClassCastException, errMsg, f)
// if status != exceptions.Caught {
// return errors.New(errMsg) // applies only if in test
// }
// }
// } else {
// // the object being checked is a class
// classPtr := classloader.MethAreaFetch(className)
// if classPtr == nil { // class wasn't loaded, so load it now
// if classloader.LoadClassFromNameOnly(className) != nil {
// glob.ErrorGoStack = string(debug.Stack())
// return errors.New("CHECKCAST: Could not load class: " + className)
// }
// classPtr = classloader.MethAreaFetch(className)
// }
//
// // if classPtr does not point to the entry for the same class, then examine superclasses
// if classPtr != classloader.MethAreaFetch(*(stringPool.GetStringPointer(obj.KlassName))) {
// if isClassAaSublclassOfB(obj.KlassName, stringPool.GetStringIndex(&className)) {
// goto checkcastOK
// }
//
// glob.ErrorGoStack = string(debug.Stack())
// errMsg := fmt.Sprintf("CHECKCAST: %s is not castable with respect to %s",
// className, classPtr.Data.Name)
// status := exceptions.ThrowEx(excNames.ClassCastException, errMsg, f)
// if status != exceptions.Caught {
// return errors.New(errMsg) // applies only if in test
// }
// } else {
// goto checkcastOK // they both point to the same class, so perforce castable
// }
// } // end of checking an object that's not an array
}
// checkcastOK:
// f.PC += 1
// continue // if CHECKCAST succeeds, do nothing

case opcodes.INSTANCEOF: // 0xC1 validate the type of object (if not nil or null)
// because this uses similar logic to CHECKCAST, any change here should
Expand Down
62 changes: 62 additions & 0 deletions src/jvm/runUtils.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"jacobin/types"
"jacobin/util"
"math"
"strings"
"unsafe"
)

Expand Down Expand Up @@ -460,3 +461,64 @@ func getSuperclasses(classNameIndex uint32) []uint32 {
}
return retval
}

func checkcastNonArrayObject(obj *object.Object, className string) bool {
// the object being checked is a class
// glob := globals.GetGlobalRef()
classPtr := classloader.MethAreaFetch(className)
if classPtr == nil { // class wasn't loaded, so load it now
if classloader.LoadClassFromNameOnly(className) != nil {
// glob.ErrorGoStack = string(debug.Stack())
// return errors.New("CHECKCAST: Could not load class: "
// + className)
return false
}
classPtr = classloader.MethAreaFetch(className)
}

// if classPtr does not point to the entry for the same class, then examine superclasses
if classPtr == classloader.MethAreaFetch(*(stringPool.GetStringPointer(obj.KlassName))) {
return true
} else if isClassAaSublclassOfB(obj.KlassName, stringPool.GetStringIndex(&className)) {
return true
}
return false
}

// do the checkcast logic for an array. The rules are:
// S = obj
// T = className
//
// If S is the type of the object referred to by objectref, and T is the resolved class, array, or
// interface type, then checkcast determines whether objectref can be cast to type T as follows:
//
// If S is an array type SC[], that is, an array of components of type SC, then:
// * If T is a class type, then T must be Object.
// * If T is an interface type, then T must be one of the interfaces implemented by arrays (JLS §4.10.3).
// * If T is an array type TC[], that is, an array of components of type TC,
// then one of the following must be true:
// > TC and SC are the same primitive type.
// > TC and SC are reference types, and type SC can be cast to TC by
// > recursive application of these rules.
func checkcastArray(obj *object.Object, className string) bool {
if obj.KlassName == types.InvalidStringIndex {
errMsg := fmt.Sprintf("CHECKCAST: expected to verify class or interface, but got none")
status := exceptions.ThrowExNil(excNames.InvalidTypeException, errMsg)
if status != exceptions.Caught {
return false // applies only if in test
}
}

sptr := stringPool.GetStringPointer(obj.KlassName)
// for the nonce if they're both the same type of arrays, we're good
// TODO: if both are arrays of reference, check the leaf types
if *sptr == className || strings.HasPrefix(className, *sptr) {
return true
}

return false
}

func checkcastInterface(obj *object.Object, className string) bool {
return false
}

0 comments on commit 0ef4103

Please sign in to comment.