Fixed baaad bug in INVOKE? handling when method does not exist
authorJan Vrany <jan.vrany@fit.cvut.cz>
Mon, 21 Mar 2016 09:21:48 +0000
changeset 3539 e546e1df7a01
parent 3538 437fc55b3eed
child 3540 d53b3e30d475
Fixed baaad bug in INVOKE? handling when method does not exist ...due to incompatible change in .class file. When a method that does not exist is about to be invoked, then `java.lang.NoSuchMethodError` must be thrown. If the instruction is INVOKEINTERFACE, `java.lang.AbstractMethodError` should be thrown instead. The VM does not check nor throw these exceptions by itself. Instead it relies on Smalltalk #doesNotUnderstand: mechanism. There it checks whether the sending context is a a JavaContext and if so, throw Java error as specified in JVM spec. Interesting how far we went without noticing...
JavaClass.st
JavaClassRefTests.st
JavaExceptionTests.st
JavaObject.st
JavaRefsAndConstantPoolTestCase.st
JavaRuntimeConstantPoolTests.st
JavaVM.st
ProxyMethod.st
--- a/JavaClass.st	Sun Mar 20 23:55:04 2016 +0000
+++ b/JavaClass.st	Mon Mar 21 09:21:48 2016 +0000
@@ -1772,7 +1772,7 @@
             directedTo: class
             for: receiver
             withArguments: args
-            from: thisContext sender
+            from: sender
             ilc: nil.
 
     ^ method isNil ifTrue:[
@@ -1783,7 +1783,7 @@
 
     "Created: / 19-09-2011 / 23:33:06 / Jan Kurs <kursjan@fit.cvut.cz>"
     "Modified: / 10-04-2012 / 16:47:31 / kursjan"
-    "Modified: / 07-01-2014 / 13:41:35 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+    "Modified: / 21-03-2016 / 01:05:16 / Jan Vrany <jan.vrany@fit.cvut.cz>"
 ! !
 
 !JavaClass methodsFor:'java initialization'!
@@ -2167,7 +2167,13 @@
 
 doesNotUnderstand:aMessage
     | sender |
-    sender := thisContext sender.
+
+    "/ When we arrive here, the stack is:
+    "/   1 - JavaObject>>doesNotUnderstand: (i.e., this method)
+    "/   2 - context for not understood method (created by VM to hold arguments)
+    "/   3 - context of method that sent the not understood message
+    "/ Hence the `thisContext sender sender below.
+    sender := thisContext sender sender.
     ^self
         perform: aMessage
         onReceiver: self
@@ -2177,7 +2183,7 @@
     "Modified: / 16-11-1998 / 16:50:56 / cg"
     "Modified: / 19-09-2011 / 23:43:56 / Jan Kurs <kursjan@fit.cvut.cz>"
     "Modified (comment): / 01-01-2012 / 19:36:41 / kursjan <kursjan@fit.cvut.cz>"
-    "Modified: / 03-01-2012 / 21:02:54 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+    "Modified: / 21-03-2016 / 00:59:41 / Jan Vrany <jan.vrany@fit.cvut.cz>"
 !
 
 lookupFieldFor:fname static: searchStatic onlyPublic: publicOnly
--- a/JavaClassRefTests.st	Sun Mar 20 23:55:04 2016 +0000
+++ b/JavaClassRefTests.st	Mon Mar 21 09:21:48 2016 +0000
@@ -84,24 +84,21 @@
 testAccessingNonPublicFromInside
     | javaClassRef  initString  throwedException |
 
-    self enableMockedExceptionThrowing.
-    
     [ initString := 'Lstx/libjava/tests/mocks/NonPublicClass;'.
     javaClassRef := self getClassRefNamed: initString.
     javaClassRef owner: (Java classForName: 'stx.libjava.tests.mocks.Crate').
     javaClassRef resolve. ] on: Error
             do: [:e | throwedException := e ].
     self assertTrue: (throwedException isNil).
-    self disableMockedExceptionThrowing.
 
     "Created: / 13-04-2011 / 13:42:47 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
     "Modified: / 23-05-2011 / 17:58:19 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
+    "Modified: / 21-03-2016 / 08:59:53 / Jan Vrany <jan.vrany@fit.cvut.cz>"
 !
 
 testAccessingNonPublicFromOutside
     | javaClassRef  initString  throwedException |
 
-    self enableMockedExceptionThrowing.
     [
         initString := 'Lstx/libjava/tests/mocks/NonPublicClass;'.
         javaClassRef := self getClassRefNamed:initString.
@@ -110,61 +107,54 @@
     ] on:Error do:[:e | throwedException := e ].
     self assertTrue:(throwedException notNil 
                 and:[ throwedException messageText = 'IllegalAccessError' ]).
-    self disableMockedExceptionThrowing.
 
     "Created: / 13-04-2011 / 13:37:41 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
     "Modified: / 23-05-2011 / 17:58:22 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
-    "Modified: / 10-12-2013 / 18:36:58 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+    "Modified: / 21-03-2016 / 08:59:56 / Jan Vrany <jan.vrany@fit.cvut.cz>"
 !
 
 testAccessingPublic
     | javaClassRef  initString |
 
-    self enableMockedExceptionThrowing.
     self shouldnt: 
             [ initString := 'Lstx/libjava/tests/mocks/PublicClass;'.
             javaClassRef := self getClassRefNamed: initString.
             javaClassRef owner: self someJavaClass.
             javaClassRef resolve. ]
         raise: Error.
-    self disableMockedExceptionThrowing.
 
     "Created: / 13-04-2011 / 13:36:33 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
     "Modified: / 23-05-2011 / 17:58:24 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
-    "Modified: / 10-12-2013 / 18:38:17 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+    "Modified: / 21-03-2016 / 09:00:00 / Jan Vrany <jan.vrany@fit.cvut.cz>"
 !
 
 testNonPublicClassPresent
     | javaClassRef  initString |
 
-    self enableMockedExceptionThrowing.
     initString := 'Lstx/libjava/tests/mocks/NonPublicClass;'.
     javaClassRef := self getClassRefNamed: initString.
     javaClassRef owner: (Java 
                 classForName: 'stx.libjava.tests.mocks.SubclassOfNonPublicClass').
     javaClassRef resolve.
     self assertTrue: (javaClassRef resolvedValue notNil).
-    self disableMockedExceptionThrowing.
 
     "Created: / 13-04-2011 / 13:38:49 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
     "Modified: / 23-05-2011 / 17:58:27 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
-    "Modified: / 31-01-2014 / 09:12:59 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+    "Modified: / 21-03-2016 / 09:00:04 / Jan Vrany <jan.vrany@fit.cvut.cz>"
 !
 
 testPublicClassPresent
     | javaClassRef  initString |
 
-    self enableMockedExceptionThrowing.
     initString := 'Lstx/libjava/tests/mocks/PublicClass;'.
     javaClassRef := self getClassRefNamed: initString.
     javaClassRef owner: self someJavaClass.
     javaClassRef resolve.
     self assertTrue: (javaClassRef resolvedValue notNil).
-    self disableMockedExceptionThrowing.
 
     "Created: / 13-04-2011 / 13:39:00 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
     "Modified: / 23-05-2011 / 17:58:29 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
-    "Modified: / 31-01-2014 / 09:13:16 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+    "Modified: / 21-03-2016 / 09:00:08 / Jan Vrany <jan.vrany@fit.cvut.cz>"
 ! !
 
 !JavaClassRefTests methodsFor:'primitives'!
--- a/JavaExceptionTests.st	Sun Mar 20 23:55:04 2016 +0000
+++ b/JavaExceptionTests.st	Mon Mar 21 09:21:48 2016 +0000
@@ -67,6 +67,7 @@
     "Created: / 30-03-2012 / 13:38:08 / Jan Vrany <jan.vrany@fit.cvut.cz>"
 ! !
 
+
 !JavaExceptionTests methodsFor:'callbacks'!
 
 call: trhower with: aBoolean 
--- a/JavaObject.st	Sun Mar 20 23:55:04 2016 +0000
+++ b/JavaObject.st	Mon Mar 21 09:21:48 2016 +0000
@@ -20,6 +20,8 @@
 "
 "{ Package: 'stx:libjava' }"
 
+"{ NameSpace: Smalltalk }"
+
 Object subclass:#JavaObject
 	instanceVariableNames:'_lockWord_'
 	classVariableNames:''
@@ -325,7 +327,29 @@
     <resource: #skipInDebuggersWalkBack>
     
     | sender retval didNotUnderstood |
-    sender := thisContext sender.
+    "/ When we arrive here, the stack is:
+    "/   1 - JavaObject>>doesNotUnderstand: (i.e., this method)
+    "/   2 - context for not understood method (created by VM to hold arguments)
+    "/   3 - context of method that sent the not understood message
+    "/ Hence the `thisContext sender sender below.
+    sender := thisContext sender sender.
+    "/ When a sending context is a JavaContext, then throw NoSuchMethodError/AbstractMethodError
+    "/ This may happen if classfile was changes meanwhile so it's now 
+    "/ incompatible
+    sender class == JavaContext ifTrue:[ 
+        "/ Sigh, must check whether the instruction was INVOKEINTERFACE which
+        "/ throws AbstractMethodError rather than NoSuchMethodError. Arghh...
+
+        | error |
+
+        (sender method byteCode at: sender pc - 1) == 185 ifTrue:[ 
+            error := 'java.lang.AbstractMethodError'
+        ] ifFalse:[ 
+            error := 'java.lang.NoSuchMethodError'
+        ].
+        JavaVM throwExceptionClassName: error withMessage: ('No method %1 in class %2' bindWith: aMessage selector with: self class javaName).
+        ^ nil
+    ].
     didNotUnderstood := false.
     retval := self class perform: aMessage onReceiver: self from: sender ifNotFound: [ didNotUnderstood :=  true ].
     ^ didNotUnderstood ifTrue:[
@@ -337,7 +361,7 @@
     "Modified: / 16-11-1998 / 16:50:56 / cg"
     "Modified: / 19-09-2011 / 23:43:56 / Jan Kurs <kursjan@fit.cvut.cz>"
     "Modified: / 01-01-2012 / 19:49:35 / kursjan <kursjan@fit.cvut.cz>"
-    "Modified: / 28-10-2013 / 10:33:56 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+    "Modified: / 21-03-2016 / 01:24:48 / Jan Vrany <jan.vrany@fit.cvut.cz>"
 ! !
 
 !JavaObject methodsFor:'printing & storing'!
--- a/JavaRefsAndConstantPoolTestCase.st	Sun Mar 20 23:55:04 2016 +0000
+++ b/JavaRefsAndConstantPoolTestCase.st	Mon Mar 21 09:21:48 2016 +0000
@@ -172,19 +172,6 @@
 
 !JavaRefsAndConstantPoolTestCase methodsFor:'helpers'!
 
-disableMockedExceptionThrowing
-    JavaResolver uniqueInstance exceptionThrower: exceptionThrowerBackup.
-
-    "Created: / 13-04-2011 / 14:11:34 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
-!
-
-enableMockedExceptionThrowing
-    exceptionThrowerBackup := JavaResolver uniqueInstance exceptionThrower.
-    JavaResolver uniqueInstance exceptionThrower: JavaExceptionThrowerMock new.
-
-    "Created: / 13-04-2011 / 14:11:01 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
-!
-
 getCrateClassReadStream
     ^ ((stx_libjava packageDirectory / 'tests' ) 
         / 'java' / 'bin' 
@@ -319,18 +306,21 @@
 !JavaRefsAndConstantPoolTestCase methodsFor:'running'!
 
 setUp
-    self enableMockedExceptionThrowing.
+    super setUp.
+    exceptionThrowerBackup := JavaResolver uniqueInstance exceptionThrower.
+    JavaResolver uniqueInstance exceptionThrower: JavaExceptionThrowerMock new.
 
     "Created: / 12-05-2011 / 17:30:41 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
     "Modified: / 23-05-2011 / 18:02:18 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
+    "Modified: / 21-03-2016 / 08:59:31 / Jan Vrany <jan.vrany@fit.cvut.cz>"
 !
 
 tearDown
-
-    self disableMockedExceptionThrowing.
+    JavaResolver uniqueInstance exceptionThrower: exceptionThrowerBackup.
+    super tearDown.
 
     "Created: / 12-05-2011 / 17:30:54 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
-    "Modified: / 08-08-2011 / 17:24:20 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+    "Modified: / 21-03-2016 / 08:59:36 / Jan Vrany <jan.vrany@fit.cvut.cz>"
 ! !
 
 !JavaRefsAndConstantPoolTestCase::JavaExceptionThrowerMock class methodsFor:'documentation'!
--- a/JavaRuntimeConstantPoolTests.st	Sun Mar 20 23:55:04 2016 +0000
+++ b/JavaRuntimeConstantPoolTests.st	Mon Mar 21 09:21:48 2016 +0000
@@ -74,10 +74,11 @@
 !
 
 tearDown
-super tearDown.
+    super tearDown.
     JavaConstantPool allConstantPools: constantPoolCacheBackup.
 
     "Created: / 13-05-2011 / 09:33:53 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
+    "Modified (format): / 21-03-2016 / 08:52:38 / Jan Vrany <jan.vrany@fit.cvut.cz>"
 ! !
 
 !JavaRuntimeConstantPoolTests methodsFor:'tests'!
--- a/JavaVM.st	Sun Mar 20 23:55:04 2016 +0000
+++ b/JavaVM.st	Mon Mar 21 09:21:48 2016 +0000
@@ -3889,10 +3889,17 @@
 !
 
 throwNoSuchMethodError
-      ^ self throwExceptionClassName: 'java.lang.NoSuchMethodError'
-              withMessage: 'looking up a method failed'.
+      ^ self throwNoSuchMethodError: 'looking up a method failed'.
 
     "Created: / 11-04-2011 / 20:33:19 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
+    "Modified: / 21-03-2016 / 01:11:31 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+!
+
+throwNoSuchMethodError: message
+      ^ self throwExceptionClassName: 'java.lang.NoSuchMethodError'
+              withMessage: message
+
+    "Created: / 21-03-2016 / 01:11:16 / Jan Vrany <jan.vrany@fit.cvut.cz>"
 !
 
 throwNullPointerException
--- a/ProxyMethod.st	Sun Mar 20 23:55:04 2016 +0000
+++ b/ProxyMethod.st	Mon Mar 21 09:21:48 2016 +0000
@@ -26,6 +26,8 @@
 "
 "{ Package: 'stx:libjava' }"
 
+"{ NameSpace: Smalltalk }"
+
 Method variableSubclass:#ProxyMethod
 	instanceVariableNames:'body'
 	classVariableNames:'InstallProxies'
@@ -148,6 +150,20 @@
     "
 
     "Created: / 06-12-2011 / 21:18:39 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+!
+
+uninstallAllProxies
+    ProxyMethod allInstances do:[:m |
+        m mclass notNil ifTrue:[
+            m mclass basicRemoveSelector: m selector  
+        ]
+    ]
+.
+    "
+    ProxyMethod uninstallAllProxies
+    "
+
+    "Created: / 21-03-2016 / 01:02:27 / Jan Vrany <jan.vrany@fit.cvut.cz>"
 ! !
 
 !ProxyMethod methodsFor:'accessing'!