From fe6db35eb42ab0ec7174e254e7eae3dbdcdb8e37 Mon Sep 17 00:00:00 2001 From: jordanmontt Date: Mon, 15 Jul 2024 17:54:02 +0200 Subject: [PATCH] Removed fast and slow path for the deactivators to avoid an infinite meta recursion --- .../MpAbstractMethodProxyTest.class.st | 20 +- .../MpAfterCounterHandler.class.st | 13 +- .../MpAfterResultHandler.class.st | 13 +- src/MethodProxies-Tests/MpClassA.class.st | 29 +- src/MethodProxies-Tests/MpClassB.class.st | 11 +- .../MpMethodProxyTest.class.st | 67 +- src/MethodProxies-Tests/MpMockObject.class.st | 9 +- .../MpTestConcurrentSharedObject.class.st | 21 +- .../MpWaitAndTriggerBeforeHandler.class.st | 9 +- .../MpWaitBeforeHandler.class.st | 9 +- src/MethodProxies-Tests/package.st | 2 +- src/MethodProxies/CompiledMethod.extension.st | 10 +- src/MethodProxies/MpCannotInstall.class.st | 13 +- src/MethodProxies/MpHandler.class.st | 17 +- src/MethodProxies/MpHiddenSelector.class.st | 9 +- src/MethodProxies/MpMethodProxy.class.st | 1291 ++--------------- src/MethodProxies/package.st | 2 +- 17 files changed, 246 insertions(+), 1299 deletions(-) diff --git a/src/MethodProxies-Tests/MpAbstractMethodProxyTest.class.st b/src/MethodProxies-Tests/MpAbstractMethodProxyTest.class.st index 10b0769..d751574 100644 --- a/src/MethodProxies-Tests/MpAbstractMethodProxyTest.class.st +++ b/src/MethodProxies-Tests/MpAbstractMethodProxyTest.class.st @@ -1,34 +1,36 @@ Class { - #name : #MpAbstractMethodProxyTest, - #superclass : #TestCase, + #name : 'MpAbstractMethodProxyTest', + #superclass : 'TestCase', #instVars : [ 'trackedWrappers' ], - #category : #'MethodProxies-Tests' + #category : 'MethodProxies-Tests', + #package : 'MethodProxies-Tests' } -{ #category : #testing } +{ #category : 'testing' } MpAbstractMethodProxyTest class >> isAbstract [ ^ self == MpAbstractMethodProxyTest ] -{ #category : #'tests - dead representation' } +{ #category : 'tests - dead representation' } MpAbstractMethodProxyTest >> installMethodProxy: aMethodProxy [ trackedWrappers add: aMethodProxy. - aMethodProxy install. - + aMethodProxy + install; + enableInstrumentation ] -{ #category : #initialization } +{ #category : 'initialization' } MpAbstractMethodProxyTest >> setUp [ super setUp. trackedWrappers := OrderedCollection new ] -{ #category : #initialization } +{ #category : 'initialization' } MpAbstractMethodProxyTest >> tearDown [ | stillInstalled | diff --git a/src/MethodProxies-Tests/MpAfterCounterHandler.class.st b/src/MethodProxies-Tests/MpAfterCounterHandler.class.st index f2054e3..16b3da7 100644 --- a/src/MethodProxies-Tests/MpAfterCounterHandler.class.st +++ b/src/MethodProxies-Tests/MpAfterCounterHandler.class.st @@ -3,26 +3,27 @@ I am a method proxy handler for testing that increments a counter after the meth I therefore test method exit " Class { - #name : #MpAfterCounterHandler, - #superclass : #MpHandler, + #name : 'MpAfterCounterHandler', + #superclass : 'MpHandler', #instVars : [ 'count' ], - #category : #'MethodProxies-Tests' + #category : 'MethodProxies-Tests', + #package : 'MethodProxies-Tests' } -{ #category : #evaluating } +{ #category : 'evaluating' } MpAfterCounterHandler >> afterMethod [ count := count + 1 ] -{ #category : #accessing } +{ #category : 'accessing' } MpAfterCounterHandler >> count [ ^ count ] -{ #category : #evaluating } +{ #category : 'evaluating' } MpAfterCounterHandler >> initialize [ super initialize. diff --git a/src/MethodProxies-Tests/MpAfterResultHandler.class.st b/src/MethodProxies-Tests/MpAfterResultHandler.class.st index 7b23acf..27629dc 100644 --- a/src/MethodProxies-Tests/MpAfterResultHandler.class.st +++ b/src/MethodProxies-Tests/MpAfterResultHandler.class.st @@ -1,24 +1,25 @@ Class { - #name : #MpAfterResultHandler, - #superclass : #MpHandler, + #name : 'MpAfterResultHandler', + #superclass : 'MpHandler', #instVars : [ 'count' ], - #category : #'MethodProxies-Tests' + #category : 'MethodProxies-Tests', + #package : 'MethodProxies-Tests' } -{ #category : #evaluating } +{ #category : 'evaluating' } MpAfterResultHandler >> afterExecutionWithReceiver: anObject arguments: anArrayOfObjects returnValue: aReturnValue [ ^ 'trapped [', aReturnValue asString, ']' ] -{ #category : #accessing } +{ #category : 'accessing' } MpAfterResultHandler >> count [ ^ count ] -{ #category : #evaluating } +{ #category : 'evaluating' } MpAfterResultHandler >> initialize [ super initialize. diff --git a/src/MethodProxies-Tests/MpClassA.class.st b/src/MethodProxies-Tests/MpClassA.class.st index 974af58..c962ac6 100644 --- a/src/MethodProxies-Tests/MpClassA.class.st +++ b/src/MethodProxies-Tests/MpClassA.class.st @@ -3,76 +3,77 @@ Instance Variables: x " Class { - #name : #MpClassA, - #superclass : #Object, + #name : 'MpClassA', + #superclass : 'Object', #instVars : [ 'x' ], - #category : #'MethodProxies-Tests' + #category : 'MethodProxies-Tests', + #package : 'MethodProxies-Tests' } -{ #category : #accessing } +{ #category : 'accessing' } MpClassA >> methodAcceptingABlock: aBlock [ ^ aBlock value ] -{ #category : #debugging } +{ #category : 'debugging' } MpClassA >> methodDelay [ (Delay forMilliseconds: 1) wait. ] -{ #category : #debugging } +{ #category : 'debugging' } MpClassA >> methodLastingOneSecond [ (Delay forMilliseconds: 0.5) wait. ^ 999 ] -{ #category : #debugging } +{ #category : 'debugging' } MpClassA >> methodOne [ ^ 101 ] -{ #category : #debugging } +{ #category : 'debugging' } MpClassA >> methodTwo [ ^ 42 ] -{ #category : #debugging } +{ #category : 'debugging' } MpClassA >> methodWithArgument: anInteger [ self x: self x + anInteger. ^ self x ] -{ #category : #debugging } +{ #category : 'debugging' } MpClassA >> methodWithException [ 1/0 ] -{ #category : #accessing } +{ #category : 'accessing' } MpClassA >> methodWithNonLocalReturn [ self methodAcceptingABlock: [ ^ self ] ] -{ #category : #debugging } +{ #category : 'debugging' } MpClassA >> methodWithWarning [ Warning new signal: 'I warned you'. ] -{ #category : #accessing } +{ #category : 'accessing' } MpClassA >> x [ ^ x ] -{ #category : #accessing } +{ #category : 'accessing' } MpClassA >> x: anInteger [ x := anInteger diff --git a/src/MethodProxies-Tests/MpClassB.class.st b/src/MethodProxies-Tests/MpClassB.class.st index 8f78cb4..95bb280 100644 --- a/src/MethodProxies-Tests/MpClassB.class.st +++ b/src/MethodProxies-Tests/MpClassB.class.st @@ -1,16 +1,17 @@ Class { - #name : #MpClassB, - #superclass : #MpClassA, - #category : #'MethodProxies-Tests' + #name : 'MpClassB', + #superclass : 'MpClassA', + #category : 'MethodProxies-Tests', + #package : 'MethodProxies-Tests' } -{ #category : #debugging } +{ #category : 'debugging' } MpClassB >> methodThree [ ^ 789 ] -{ #category : #debugging } +{ #category : 'debugging' } MpClassB >> methodTwo [ ^ 84 diff --git a/src/MethodProxies-Tests/MpMethodProxyTest.class.st b/src/MethodProxies-Tests/MpMethodProxyTest.class.st index 5824ce4..0c6a6ab 100644 --- a/src/MethodProxies-Tests/MpMethodProxyTest.class.st +++ b/src/MethodProxies-Tests/MpMethodProxyTest.class.st @@ -1,15 +1,16 @@ Class { - #name : #MpMethodProxyTest, - #superclass : #MpAbstractMethodProxyTest, - #category : #'MethodProxies-Tests' + #name : 'MpMethodProxyTest', + #superclass : 'MpAbstractMethodProxyTest', + #category : 'MethodProxies-Tests', + #package : 'MethodProxies-Tests' } -{ #category : #asserting } +{ #category : 'asserting' } MpMethodProxyTest class >> shouldInheritSelectors [ ^ true ] -{ #category : #'tests - safety' } +{ #category : 'tests - safety' } MpMethodProxyTest >> assertCannotInstall: mp [ [mp install. @@ -18,18 +19,18 @@ MpMethodProxyTest >> assertCannotInstall: mp [ self assert: ex methodProxy equals: mp ] ] -{ #category : #'tests - safety' } +{ #category : 'tests - safety' } MpMethodProxyTest >> callEnsureWithNonLocalReturn [ [ ^ 7 ] ensure: [ 2 ] ] -{ #category : #hook } +{ #category : 'hook' } MpMethodProxyTest >> handlerClass [ ^ MpHandler ] -{ #category : #'tests - safety' } +{ #category : 'tests - safety' } MpMethodProxyTest >> testCanRunConcurrently [ "This tests the ability of method proxies to not influence each other between threads." @@ -77,7 +78,7 @@ MpMethodProxyTest >> testCanRunConcurrently [ self assert: handlerTrigger2 count equals: 0 ] -{ #category : #'tests - safety' } +{ #category : 'tests - safety' } MpMethodProxyTest >> testCanWrapAboutToReturnThroughWithNonLocalReturn [ | mp handler | @@ -90,7 +91,7 @@ MpMethodProxyTest >> testCanWrapAboutToReturnThroughWithNonLocalReturn [ self assert: handler count equals: 1 ] -{ #category : #'tests - safety' } +{ #category : 'tests - safety' } MpMethodProxyTest >> testCanWrapBasicNew [ | mp handler | @@ -104,7 +105,7 @@ MpMethodProxyTest >> testCanWrapBasicNew [ self assert: handler count equals: 1 ] -{ #category : #'tests - safety' } +{ #category : 'tests - safety' } MpMethodProxyTest >> testCanWrapEnsure [ | mp handler | @@ -117,7 +118,7 @@ MpMethodProxyTest >> testCanWrapEnsure [ self assert: handler count equals: 1 ] -{ #category : #'tests - safety' } +{ #category : 'tests - safety' } MpMethodProxyTest >> testCanWrapEnsureNonLocalReturn [ | mp handler | @@ -130,7 +131,7 @@ MpMethodProxyTest >> testCanWrapEnsureNonLocalReturn [ self assert: handler count equals: 1 ] -{ #category : #'tests - safety' } +{ #category : 'tests - safety' } MpMethodProxyTest >> testCanWrapEnsureWithException [ | mp handler | @@ -143,7 +144,7 @@ MpMethodProxyTest >> testCanWrapEnsureWithException [ self assert: handler count equals: 1 ] -{ #category : #'tests - safety' } +{ #category : 'tests - safety' } MpMethodProxyTest >> testCanWrapValue [ @@ -157,7 +158,7 @@ MpMethodProxyTest >> testCanWrapValue [ self assert: handler count equals: 2 ] -{ #category : #'tests - safety' } +{ #category : 'tests - safety' } MpMethodProxyTest >> testCanWrapValueWithException [ @@ -175,7 +176,7 @@ MpMethodProxyTest >> testCanWrapValueWithException [ self assert: handler count equals: 2 ] -{ #category : #'tests - safety' } +{ #category : 'tests - safety' } MpMethodProxyTest >> testCannotWrapCriticalProxyMethods6 [ | mp handler | @@ -184,7 +185,7 @@ MpMethodProxyTest >> testCannotWrapCriticalProxyMethods6 [ self assertCannotInstall: mp ] -{ #category : #'tests - dead representation' } +{ #category : 'tests - dead representation' } MpMethodProxyTest >> testCreatingAnInstanceDoesNotInstallIt [ | mp | @@ -197,7 +198,7 @@ MpMethodProxyTest >> testCreatingAnInstanceDoesNotInstallIt [ self deny: mp isInstalled ] -{ #category : #'tests - safety' } +{ #category : 'tests - safety' } MpMethodProxyTest >> testExceptionsAfterInstrumentationDoNotBreakInstrumentation [ | mp handler | @@ -214,7 +215,7 @@ MpMethodProxyTest >> testExceptionsAfterInstrumentationDoNotBreakInstrumentation self assert: handler count equals: 2 ] -{ #category : #'tests - safety' } +{ #category : 'tests - safety' } MpMethodProxyTest >> testExceptionsAfterInstrumentationFlow [ "Managing exceptions in the wrapper" | p | @@ -231,7 +232,7 @@ MpMethodProxyTest >> testExceptionsAfterInstrumentationFlow [ self assert: error messageText equals: 'error after instrumentation' ].] ] -{ #category : #'tests - safety' } +{ #category : 'tests - safety' } MpMethodProxyTest >> testExceptionsDuringInstrumentationDoNotBreakInstrumentation [ "Managing exceptions in the wrapper" | w | @@ -248,7 +249,7 @@ MpMethodProxyTest >> testExceptionsDuringInstrumentationDoNotBreakInstrumentatio self assert: error messageText equals: 'error during instrumentation' ].] ] -{ #category : #'tests - installation' } +{ #category : 'tests - installation' } MpMethodProxyTest >> testInstallSetCompiledMethod [ | mw method | @@ -262,7 +263,7 @@ MpMethodProxyTest >> testInstallSetCompiledMethod [ mw uninstall ] ] -{ #category : #'tests - installation' } +{ #category : 'tests - installation' } MpMethodProxyTest >> testIsInstalled [ | mw method | @@ -274,7 +275,7 @@ MpMethodProxyTest >> testIsInstalled [ self assert: mw isInstalled ] ensure: [ mw uninstall ] ] -{ #category : #'tests - installation' } +{ #category : 'tests - installation' } MpMethodProxyTest >> testIsInstalledNestedMWAreNotInstalled [ | mp mp2 | @@ -298,7 +299,7 @@ MpMethodProxyTest >> testIsInstalledNestedMWAreNotInstalled [ self deny: mp isInstalled ] ] -{ #category : #'tests - safety' } +{ #category : 'tests - safety' } MpMethodProxyTest >> testRecursiveMethodWrapperDoesNotRecurse [ | mw method | @@ -309,7 +310,7 @@ MpMethodProxyTest >> testRecursiveMethodWrapperDoesNotRecurse [ self assert: MpMockObject new recursiveMethod equals: 'trapped [original]'. ] -{ #category : #'tests - installation' } +{ #category : 'tests - installation' } MpMethodProxyTest >> testUninstall [ | mp method | @@ -325,7 +326,7 @@ MpMethodProxyTest >> testUninstall [ self assert: (MpClassA compiledMethodAt: #methodOne) == method ] ] -{ #category : #'tests - installation' } +{ #category : 'tests - installation' } MpMethodProxyTest >> testUninstallNestedInRightOrderIsOk [ | mp mp2 method | @@ -353,7 +354,7 @@ MpMethodProxyTest >> testUninstallNestedInRightOrderIsOk [ identicalTo: method ] -{ #category : #tests } +{ #category : 'tests' } MpMethodProxyTest >> testUnwrappedMethodAtOneLevelIsTheWrappedMethod [ | mp method | @@ -364,7 +365,7 @@ MpMethodProxyTest >> testUnwrappedMethodAtOneLevelIsTheWrappedMethod [ self assert: mp proxifiedMethod equals: method ] -{ #category : #tests } +{ #category : 'tests' } MpMethodProxyTest >> testUnwrappedMethodOfNestedMethodWrapperInTheCompiledMethod [ | mp method mp2 | @@ -382,7 +383,7 @@ MpMethodProxyTest >> testUnwrappedMethodOfNestedMethodWrapperInTheCompiledMethod self assert: mp2 proxifiedMethod equals: mp trapMethod. ] -{ #category : #'tests - safety' } +{ #category : 'tests - safety' } MpMethodProxyTest >> testWrapEssentialPrimitiveShouldBeCalledDuringInstallation [ | mw method handler | @@ -393,7 +394,7 @@ MpMethodProxyTest >> testWrapEssentialPrimitiveShouldBeCalledDuringInstallation self deny: handler called ] -{ #category : #'tests - safety' } +{ #category : 'tests - safety' } MpMethodProxyTest >> testWrapEssentialPrimitiveShouldNotRecurse [ | mw method handler | @@ -408,7 +409,7 @@ MpMethodProxyTest >> testWrapEssentialPrimitiveShouldNotRecurse [ self assert: handler called ] -{ #category : #'tests - safety' } +{ #category : 'tests - safety' } MpMethodProxyTest >> testWrapMethodCalledDuringInstallationIsNotIntercepted [ | mw method handler | @@ -419,7 +420,7 @@ MpMethodProxyTest >> testWrapMethodCalledDuringInstallationIsNotIntercepted [ self deny: handler called ] -{ #category : #'tests - safety' } +{ #category : 'tests - safety' } MpMethodProxyTest >> testWrapNonLocalReturns [ | mp handler | @@ -434,7 +435,7 @@ MpMethodProxyTest >> testWrapNonLocalReturns [ self assert: handler count equals: 1 ] -{ #category : #tests } +{ #category : 'tests' } MpMethodProxyTest >> testWrappingTwiceIsPossible [ | mp1 method mp2 | diff --git a/src/MethodProxies-Tests/MpMockObject.class.st b/src/MethodProxies-Tests/MpMockObject.class.st index 733f17f..8fa7fb4 100644 --- a/src/MethodProxies-Tests/MpMockObject.class.st +++ b/src/MethodProxies-Tests/MpMockObject.class.st @@ -1,10 +1,11 @@ Class { - #name : #MpMockObject, - #superclass : #Object, - #category : #'MethodProxies-Tests' + #name : 'MpMockObject', + #superclass : 'Object', + #category : 'MethodProxies-Tests', + #package : 'MethodProxies-Tests' } -{ #category : #G } +{ #category : 'G' } MpMockObject >> recursiveMethod [ ^ 'original' diff --git a/src/MethodProxies-Tests/MpTestConcurrentSharedObject.class.st b/src/MethodProxies-Tests/MpTestConcurrentSharedObject.class.st index c578fc1..fb5f3cf 100644 --- a/src/MethodProxies-Tests/MpTestConcurrentSharedObject.class.st +++ b/src/MethodProxies-Tests/MpTestConcurrentSharedObject.class.st @@ -1,31 +1,32 @@ Class { - #name : #MpTestConcurrentSharedObject, - #superclass : #Object, + #name : 'MpTestConcurrentSharedObject', + #superclass : 'Object', #instVars : [ 'concurrencySemaphore' ], - #category : #'MethodProxies-Tests' + #category : 'MethodProxies-Tests', + #package : 'MethodProxies-Tests' } -{ #category : #accessing } +{ #category : 'accessing' } MpTestConcurrentSharedObject >> concurrencySemaphore [ ^ concurrencySemaphore ] -{ #category : #accessing } +{ #category : 'accessing' } MpTestConcurrentSharedObject >> concurrencySemaphore: anObject [ concurrencySemaphore := anObject ] -{ #category : #initialization } +{ #category : 'initialization' } MpTestConcurrentSharedObject >> initialize [ self concurrencySemaphore: Semaphore new ] -{ #category : #debugging } +{ #category : 'debugging' } MpTestConcurrentSharedObject >> methodProcess1 [ "We are in instrumented territory this trigger should be registered." self trigger1. @@ -34,17 +35,17 @@ MpTestConcurrentSharedObject >> methodProcess1 [ concurrencySemaphore wait. ] -{ #category : #debugging } +{ #category : 'debugging' } MpTestConcurrentSharedObject >> methodProcess2 [ "Do nothing" ] -{ #category : #debugging } +{ #category : 'debugging' } MpTestConcurrentSharedObject >> trigger1 [ "Proxified method being counted" ] -{ #category : #debugging } +{ #category : 'debugging' } MpTestConcurrentSharedObject >> trigger2 [ "Proxified method being counted" ] diff --git a/src/MethodProxies-Tests/MpWaitAndTriggerBeforeHandler.class.st b/src/MethodProxies-Tests/MpWaitAndTriggerBeforeHandler.class.st index fb09c3f..2d19708 100644 --- a/src/MethodProxies-Tests/MpWaitAndTriggerBeforeHandler.class.st +++ b/src/MethodProxies-Tests/MpWaitAndTriggerBeforeHandler.class.st @@ -1,10 +1,11 @@ Class { - #name : #MpWaitAndTriggerBeforeHandler, - #superclass : #MpHandler, - #category : #'MethodProxies-Tests' + #name : 'MpWaitAndTriggerBeforeHandler', + #superclass : 'MpHandler', + #category : 'MethodProxies-Tests', + #package : 'MethodProxies-Tests' } -{ #category : #evaluating } +{ #category : 'evaluating' } MpWaitAndTriggerBeforeHandler >> beforeExecutionWithReceiver: anObject arguments: anArrayOfObjects [ "Begin execution, stop instrumentation and let first thread enter its method." anObject concurrencySemaphore signal. diff --git a/src/MethodProxies-Tests/MpWaitBeforeHandler.class.st b/src/MethodProxies-Tests/MpWaitBeforeHandler.class.st index baf5aaf..fb6d5b9 100644 --- a/src/MethodProxies-Tests/MpWaitBeforeHandler.class.st +++ b/src/MethodProxies-Tests/MpWaitBeforeHandler.class.st @@ -1,10 +1,11 @@ Class { - #name : #MpWaitBeforeHandler, - #superclass : #MpHandler, - #category : #'MethodProxies-Tests' + #name : 'MpWaitBeforeHandler', + #superclass : 'MpHandler', + #category : 'MethodProxies-Tests', + #package : 'MethodProxies-Tests' } -{ #category : #evaluating } +{ #category : 'evaluating' } MpWaitBeforeHandler >> beforeExecutionWithReceiver: anObject arguments: anArrayOfObjects [ "We are inside the before, not being instrumented, wait for second thread to enter inside before." anObject concurrencySemaphore wait diff --git a/src/MethodProxies-Tests/package.st b/src/MethodProxies-Tests/package.st index d10978f..be06d04 100644 --- a/src/MethodProxies-Tests/package.st +++ b/src/MethodProxies-Tests/package.st @@ -1 +1 @@ -Package { #name : #'MethodProxies-Tests' } +Package { #name : 'MethodProxies-Tests' } diff --git a/src/MethodProxies/CompiledMethod.extension.st b/src/MethodProxies/CompiledMethod.extension.st index a9c61ea..f993dfb 100644 --- a/src/MethodProxies/CompiledMethod.extension.st +++ b/src/MethodProxies/CompiledMethod.extension.st @@ -1,24 +1,24 @@ -Extension { #name : #CompiledMethod } +Extension { #name : 'CompiledMethod' } -{ #category : #'*MethodProxies' } +{ #category : '*MethodProxies' } CompiledMethod >> install [ "does not nothing. Act as a NullObject for MethodProxy." ^ self ] -{ #category : #'*MethodProxies' } +{ #category : '*MethodProxies' } CompiledMethod >> isMethodProxy [ ^ false ] -{ #category : #'*MethodProxies' } +{ #category : '*MethodProxies' } CompiledMethod >> uninstall [ "does not nothing. Act as a NullObject for MethodProxy." ^ self ] -{ #category : #'*MethodProxies' } +{ #category : '*MethodProxies' } CompiledMethod >> unproxifiedMethod [ ^ self ] diff --git a/src/MethodProxies/MpCannotInstall.class.st b/src/MethodProxies/MpCannotInstall.class.st index 5b25ec0..acfe996 100644 --- a/src/MethodProxies/MpCannotInstall.class.st +++ b/src/MethodProxies/MpCannotInstall.class.st @@ -3,15 +3,16 @@ I'm an exception raised when a proxy cannot be installed. This usually happens when the wrapped method is a very special method whose instrumentation could break the system. " Class { - #name : #MpCannotInstall, - #superclass : #Error, + #name : 'MpCannotInstall', + #superclass : 'Error', #instVars : [ 'methodProxy' ], - #category : #MethodProxies + #category : 'MethodProxies', + #package : 'MethodProxies' } -{ #category : #signalling } +{ #category : 'signalling' } MpCannotInstall class >> signalWith: aMethodProxy [ self new @@ -19,13 +20,13 @@ MpCannotInstall class >> signalWith: aMethodProxy [ signal ] -{ #category : #accessing } +{ #category : 'accessing' } MpCannotInstall >> methodProxy [ ^ methodProxy ] -{ #category : #accessing } +{ #category : 'accessing' } MpCannotInstall >> methodProxy: anObject [ methodProxy := anObject diff --git a/src/MethodProxies/MpHandler.class.st b/src/MethodProxies/MpHandler.class.st index f906d8a..0a127d2 100644 --- a/src/MethodProxies/MpHandler.class.st +++ b/src/MethodProxies/MpHandler.class.st @@ -13,35 +13,36 @@ Moreover, for cases when this is not required, two higher-level hooks are propos - `afterMethod`: called before the method returns, either by normal or abnormal execution. It does not allow any action on the return value. " Class { - #name : #MpHandler, - #superclass : #Object, - #category : #MethodProxies + #name : 'MpHandler', + #superclass : 'Object', + #category : 'MethodProxies', + #package : 'MethodProxies' } -{ #category : #evaluating } +{ #category : 'evaluating' } MpHandler >> aboutToReturnWithReceiver: receiver arguments: arguments [ self afterMethod ] -{ #category : #evaluating } +{ #category : 'evaluating' } MpHandler >> afterExecutionWithReceiver: anObject arguments: anArrayOfObjects returnValue: aReturnValue [ self afterMethod. ^ aReturnValue ] -{ #category : #evaluating } +{ #category : 'evaluating' } MpHandler >> afterMethod [ "Perform action after execution." ] -{ #category : #evaluating } +{ #category : 'evaluating' } MpHandler >> beforeExecutionWithReceiver: anObject arguments: anArrayOfObjects [ self beforeMethod ] -{ #category : #evaluating } +{ #category : 'evaluating' } MpHandler >> beforeMethod [ ] diff --git a/src/MethodProxies/MpHiddenSelector.class.st b/src/MethodProxies/MpHiddenSelector.class.st index cc01a9e..9664131 100644 --- a/src/MethodProxies/MpHiddenSelector.class.st +++ b/src/MethodProxies/MpHiddenSelector.class.st @@ -6,12 +6,13 @@ I'm used as key of the original method wrapped by proxies, to ensure this wrappe Check my usages during proxy installation. " Class { - #name : #MpHiddenSelector, - #superclass : #Object, - #category : #MethodProxies + #name : 'MpHiddenSelector', + #superclass : 'Object', + #category : 'MethodProxies', + #package : 'MethodProxies' } -{ #category : #private } +{ #category : 'private' } MpHiddenSelector >> flushCache [ "Tell the virtual machine to remove all entries with this symbol as a selector from its method lookup caches, if it has any. This must be done whenever a method is added, redefined or removed, so that message lookups reflect the revised organization. c.f. Behavior>>flushCache & CompiledMethod>>flushCache. Essential. See MethodDictionary class comment." diff --git a/src/MethodProxies/MpMethodProxy.class.st b/src/MethodProxies/MpMethodProxy.class.st index 08cb0c4..4449150 100644 --- a/src/MethodProxies/MpMethodProxy.class.st +++ b/src/MethodProxies/MpMethodProxy.class.st @@ -52,8 +52,8 @@ On top of this, the handler of traps is set as a literal that is patched. That literal is a normal object (`MpProxyInstrumentationDeactivator`) that on value inspects the stack (we are in the slow case anyways, who cares! :D) and performs what is required to do. " Class { - #name : #MpMethodProxy, - #superclass : #Object, + #name : 'MpMethodProxy', + #superclass : 'Object', #instVars : [ 'handler', 'hiddenSelector', @@ -62,12 +62,13 @@ Class { 'proxifiedMethod' ], #classVars : [ - 'MetaOwner' + 'ENABLED' ], - #category : #MethodProxies + #category : 'MethodProxies', + #package : 'MethodProxies' } -{ #category : #evaluating } +{ #category : 'evaluating' } MpMethodProxy class >> buildPrototypesUpToArguments: maxNumberOfArguments [ "This method builds/compiles the prototype traps for arguments up to the argument. The trap prototypes are installed in this class' class side. @@ -99,7 +100,25 @@ MpMethodProxy class >> buildPrototypesUpToArguments: maxNumberOfArguments [ self class compile: originalAst formattedCode ] ] -{ #category : #'instance creation' } +{ #category : 'as yet unclassified' } +MpMethodProxy class >> disableInstrumentation [ + + ENABLED := false +] + +{ #category : 'as yet unclassified' } +MpMethodProxy class >> enableInstrumentation [ + + ENABLED := true +] + +{ #category : 'class initialization' } +MpMethodProxy class >> initialize [ + + self disableInstrumentation +] + +{ #category : 'instance creation' } MpMethodProxy class >> onMethod: aMethod handler: aHandler [ ^ self new @@ -108,73 +127,15 @@ MpMethodProxy class >> onMethod: aMethod handler: aHandler [ yourself ] -{ #category : #evaluating } +{ #category : 'evaluating' } MpMethodProxy class >> prototypeTrap [ "The unwind handler should be the first temp, the complete flag should be the second temp. Then this method is free to use as many extra temporaries and arguments as is wants" | deactivator complete result process wasMeta | - "Set the deactivator literal for the fast path. - It will be patched to an exception handler" - deactivator := #fastdeactivator. - - "Quick check, if we are not in a meta-level, we are the first one here! - Chances are there are never meta-recursions. - Mark it and go FAST" - MetaOwner ifNil: [ "Take the ownership of the meta-level to call before" - MetaOwner := Processor activeProcess. - #handler beforeExecutionWithReceiver: self arguments: #( ). - - "Release it before forwarding the call" - MetaOwner := nil. - result := self originalMessage. - - "Try to get it back and do the fast path if that's the case." - MetaOwner ifNil: [ - MetaOwner := Processor activeProcess. - result := #handler - afterExecutionWithReceiver: self - arguments: #( ) - returnValue: result. - MetaOwner := nil. - ^ result ]. - - "However, maybe another process took it. - In that case we will need to fall back to the slow case" - - "Set the deactivator literal for the slow path. - It will be patched to an exception handler" - deactivator := #slowdeactivator. - - - process := Processor activeProcess. - "Move to the meta level and call the after hooks. - Two after hooks are required. - One indicates the method is returning either because a normal return, - or a stack unwind due to exceptions or non-local returns - The other indicates we are returning normally with a value." - process shiftLevelUp. - wasMeta := true. - result := #handler - afterExecutionWithReceiver: self - arguments: #( ) - returnValue: result. - process shiftLevelDown. - wasMeta := false. - - "Mark the execution as complete to avoid double execution of the unwind handler" - complete := true. - ^ result ]. - - "If we are here, this is maybe a meta call. - Two possibilities: - - we are in the same thread of another meta call: we should just forward without instrumenting - - we are in another thread: increase the meta-level normally" - process := Processor activeProcess. - MetaOwner == Processor activeProcess ifTrue: [ - ^ self originalMessage ]. + process isMeta ifTrue: [ ^ self originalMessage ]. "Set the deactivator literal for the slow path. It will be patched to an exception handler" @@ -212,7 +173,7 @@ MpMethodProxy class >> prototypeTrap [ ^ result ] -{ #category : #'as yet unclassified' } +{ #category : 'as yet unclassified' } MpMethodProxy class >> proxyMethod: method handler: aHandler [ ^ MpMethodProxy new @@ -221,71 +182,16 @@ MpMethodProxy class >> proxyMethod: method handler: aHandler [ yourself ] -{ #category : #'as yet unclassified' } +{ #category : 'as yet unclassified' } MpMethodProxy class >> trapMethod [ "The unwind handler should be the first temp, the complete flag should be the second temp. Then this method is free to use as many extra temporaries and arguments as is wants" | deactivator complete result process wasMeta | - "Set the deactivator literal for the fast path. - It will be patched to an exception handler" - deactivator := #fastdeactivator. process := Processor activeProcess. process isMeta ifTrue: [ ^ self trapMethod ]. - "Quick check, if we are not in a meta-level, we are the first one here! - Chances are there are never meta-recursions. - Mark it and go FAST" - MetaOwner ifNil: [ "Take the ownership of the meta-level to call before" - MetaOwner := process. - #handler beforeExecutionWithReceiver: self arguments: #( ). - - "Release it before forwarding the call" - MetaOwner := nil. - result := self trapMethod. - - "Try to get it back and do the fast path if that's the case." - MetaOwner ifNil: [ - MetaOwner := process. - result := #handler - afterExecutionWithReceiver: self - arguments: #( ) - returnValue: result. - MetaOwner := nil. - ^ result ]. - - "However, maybe another process took it. - In that case we will need to fall back to the slow case" - - "Set the deactivator literal for the slow path. - It will be patched to an exception handler" - deactivator := #slowdeactivator. - - "Move to the meta level and call the after hooks. - Two after hooks are required. - One indicates the method is returning either because a normal return, - or a stack unwind due to exceptions or non-local returns - The other indicates we are returning normally with a value." - process shiftLevelUp. - wasMeta := true. - result := #handler - afterExecutionWithReceiver: self - arguments: #( ) - returnValue: result. - process shiftLevelDown. - wasMeta := false. - - "Mark the execution as complete to avoid double execution of the unwind handler" - complete := true. - ^ result ]. - - "If we are here, this is maybe a meta call. - Two possibilities: - - we are in the same thread of another meta call: we should just forward without instrumenting - - we are in another thread: increase the meta-level normally" - MetaOwner == process ifTrue: [ ^ self trapMethod ]. - "Set the deactivator literal for the slow path. It will be patched to an exception handler" deactivator := #slowdeactivator. @@ -322,73 +228,15 @@ MpMethodProxy class >> trapMethod [ ^ result ] -{ #category : #'as yet unclassified' } +{ #category : 'as yet unclassified' } MpMethodProxy class >> trapMethodwith: arg1 [ "The unwind handler should be the first temp, the complete flag should be the second temp. Then this method is free to use as many extra temporaries and arguments as is wants" | deactivator complete result process wasMeta | - "Set the deactivator literal for the fast path. - It will be patched to an exception handler" - deactivator := #fastdeactivator. - - "Quick check, if we are not in a meta-level, we are the first one here! - Chances are there are never meta-recursions. - Mark it and go FAST" - MetaOwner ifNil: [ "Take the ownership of the meta-level to call before" - MetaOwner := Processor activeProcess. - #handler beforeExecutionWithReceiver: self arguments: #( ). - - "Release it before forwarding the call" - MetaOwner := nil. - result := self trapMethodwith: arg1. - - "Try to get it back and do the fast path if that's the case." - MetaOwner ifNil: [ - MetaOwner := Processor activeProcess. - result := #handler - afterExecutionWithReceiver: self - arguments: #( ) - returnValue: result. - MetaOwner := nil. - ^ result ]. - - "However, maybe another process took it. - In that case we will need to fall back to the slow case" - - "Set the deactivator literal for the slow path. - It will be patched to an exception handler" - deactivator := #slowdeactivator. - - - process := Processor activeProcess. - "Move to the meta level and call the after hooks. - Two after hooks are required. - One indicates the method is returning either because a normal return, - or a stack unwind due to exceptions or non-local returns - The other indicates we are returning normally with a value." - process shiftLevelUp. - wasMeta := true. - result := #handler - afterExecutionWithReceiver: self - arguments: #( ) - returnValue: result. - process shiftLevelDown. - wasMeta := false. - - "Mark the execution as complete to avoid double execution of the unwind handler" - complete := true. - ^ result ]. - - "If we are here, this is maybe a meta call. - Two possibilities: - - we are in the same thread of another meta call: we should just forward without instrumenting - - we are in another thread: increase the meta-level normally" - process := Processor activeProcess. - MetaOwner == Processor activeProcess ifTrue: [ - ^ self trapMethodwith: arg1 ]. + process isMeta ifTrue: [ ^ self trapMethodwith: arg1 ]. "Set the deactivator literal for the slow path. It will be patched to an exception handler" @@ -426,73 +274,15 @@ MpMethodProxy class >> trapMethodwith: arg1 [ ^ result ] -{ #category : #'as yet unclassified' } +{ #category : 'as yet unclassified' } MpMethodProxy class >> trapMethodwith: arg1 with: arg2 [ "The unwind handler should be the first temp, the complete flag should be the second temp. Then this method is free to use as many extra temporaries and arguments as is wants" | deactivator complete result process wasMeta | - "Set the deactivator literal for the fast path. - It will be patched to an exception handler" - deactivator := #fastdeactivator. - - "Quick check, if we are not in a meta-level, we are the first one here! - Chances are there are never meta-recursions. - Mark it and go FAST" - MetaOwner ifNil: [ "Take the ownership of the meta-level to call before" - MetaOwner := Processor activeProcess. - #handler beforeExecutionWithReceiver: self arguments: #( ). - - "Release it before forwarding the call" - MetaOwner := nil. - result := self trapMethodwith: arg1 with: arg2. - - "Try to get it back and do the fast path if that's the case." - MetaOwner ifNil: [ - MetaOwner := Processor activeProcess. - result := #handler - afterExecutionWithReceiver: self - arguments: #( ) - returnValue: result. - MetaOwner := nil. - ^ result ]. - - "However, maybe another process took it. - In that case we will need to fall back to the slow case" - - "Set the deactivator literal for the slow path. - It will be patched to an exception handler" - deactivator := #slowdeactivator. - - - process := Processor activeProcess. - "Move to the meta level and call the after hooks. - Two after hooks are required. - One indicates the method is returning either because a normal return, - or a stack unwind due to exceptions or non-local returns - The other indicates we are returning normally with a value." - process shiftLevelUp. - wasMeta := true. - result := #handler - afterExecutionWithReceiver: self - arguments: #( ) - returnValue: result. - process shiftLevelDown. - wasMeta := false. - - "Mark the execution as complete to avoid double execution of the unwind handler" - complete := true. - ^ result ]. - - "If we are here, this is maybe a meta call. - Two possibilities: - - we are in the same thread of another meta call: we should just forward without instrumenting - - we are in another thread: increase the meta-level normally" - process := Processor activeProcess. - MetaOwner == Processor activeProcess ifTrue: [ - ^ self trapMethodwith: arg1 with: arg2 ]. + process isMeta ifTrue: [ ^ self trapMethodwith: arg1 with: arg2 ]. "Set the deactivator literal for the slow path. It will be patched to an exception handler" @@ -530,72 +320,15 @@ MpMethodProxy class >> trapMethodwith: arg1 with: arg2 [ ^ result ] -{ #category : #'as yet unclassified' } +{ #category : 'as yet unclassified' } MpMethodProxy class >> trapMethodwith: arg1 with: arg2 with: arg3 [ "The unwind handler should be the first temp, the complete flag should be the second temp. Then this method is free to use as many extra temporaries and arguments as is wants" | deactivator complete result process wasMeta | - "Set the deactivator literal for the fast path. - It will be patched to an exception handler" - deactivator := #fastdeactivator. - - "Quick check, if we are not in a meta-level, we are the first one here! - Chances are there are never meta-recursions. - Mark it and go FAST" - MetaOwner ifNil: [ "Take the ownership of the meta-level to call before" - MetaOwner := Processor activeProcess. - #handler beforeExecutionWithReceiver: self arguments: #( ). - - "Release it before forwarding the call" - MetaOwner := nil. - result := self trapMethodwith: arg1 with: arg2 with: arg3. - - "Try to get it back and do the fast path if that's the case." - MetaOwner ifNil: [ - MetaOwner := Processor activeProcess. - result := #handler - afterExecutionWithReceiver: self - arguments: #( ) - returnValue: result. - MetaOwner := nil. - ^ result ]. - - "However, maybe another process took it. - In that case we will need to fall back to the slow case" - - "Set the deactivator literal for the slow path. - It will be patched to an exception handler" - deactivator := #slowdeactivator. - - - process := Processor activeProcess. - "Move to the meta level and call the after hooks. - Two after hooks are required. - One indicates the method is returning either because a normal return, - or a stack unwind due to exceptions or non-local returns - The other indicates we are returning normally with a value." - process shiftLevelUp. - wasMeta := true. - result := #handler - afterExecutionWithReceiver: self - arguments: #( ) - returnValue: result. - process shiftLevelDown. - wasMeta := false. - - "Mark the execution as complete to avoid double execution of the unwind handler" - complete := true. - ^ result ]. - - "If we are here, this is maybe a meta call. - Two possibilities: - - we are in the same thread of another meta call: we should just forward without instrumenting - - we are in another thread: increase the meta-level normally" - process := Processor activeProcess. - MetaOwner == Processor activeProcess ifTrue: [ + process isMeta ifTrue: [ ^ self trapMethodwith: arg1 with: arg2 with: arg3 ]. "Set the deactivator literal for the slow path. @@ -634,76 +367,15 @@ MpMethodProxy class >> trapMethodwith: arg1 with: arg2 with: arg3 [ ^ result ] -{ #category : #'as yet unclassified' } +{ #category : 'as yet unclassified' } MpMethodProxy class >> trapMethodwith: arg1 with: arg2 with: arg3 with: arg4 [ "The unwind handler should be the first temp, the complete flag should be the second temp. Then this method is free to use as many extra temporaries and arguments as is wants" | deactivator complete result process wasMeta | - "Set the deactivator literal for the fast path. - It will be patched to an exception handler" - deactivator := #fastdeactivator. - - "Quick check, if we are not in a meta-level, we are the first one here! - Chances are there are never meta-recursions. - Mark it and go FAST" - MetaOwner ifNil: [ "Take the ownership of the meta-level to call before" - MetaOwner := Processor activeProcess. - #handler beforeExecutionWithReceiver: self arguments: #( ). - - "Release it before forwarding the call" - MetaOwner := nil. - result := self - trapMethodwith: arg1 - with: arg2 - with: arg3 - with: arg4. - - "Try to get it back and do the fast path if that's the case." - MetaOwner ifNil: [ - MetaOwner := Processor activeProcess. - result := #handler - afterExecutionWithReceiver: self - arguments: #( ) - returnValue: result. - MetaOwner := nil. - ^ result ]. - - "However, maybe another process took it. - In that case we will need to fall back to the slow case" - - "Set the deactivator literal for the slow path. - It will be patched to an exception handler" - deactivator := #slowdeactivator. - - - process := Processor activeProcess. - "Move to the meta level and call the after hooks. - Two after hooks are required. - One indicates the method is returning either because a normal return, - or a stack unwind due to exceptions or non-local returns - The other indicates we are returning normally with a value." - process shiftLevelUp. - wasMeta := true. - result := #handler - afterExecutionWithReceiver: self - arguments: #( ) - returnValue: result. - process shiftLevelDown. - wasMeta := false. - - "Mark the execution as complete to avoid double execution of the unwind handler" - complete := true. - ^ result ]. - - "If we are here, this is maybe a meta call. - Two possibilities: - - we are in the same thread of another meta call: we should just forward without instrumenting - - we are in another thread: increase the meta-level normally" - process := Processor activeProcess. - MetaOwner == Processor activeProcess ifTrue: [ + process isMeta ifTrue: [ ^ self trapMethodwith: arg1 with: arg2 @@ -750,77 +422,15 @@ MpMethodProxy class >> trapMethodwith: arg1 with: arg2 with: arg3 with: arg4 [ ^ result ] -{ #category : #'as yet unclassified' } +{ #category : 'as yet unclassified' } MpMethodProxy class >> trapMethodwith: arg1 with: arg2 with: arg3 with: arg4 with: arg5 [ "The unwind handler should be the first temp, the complete flag should be the second temp. Then this method is free to use as many extra temporaries and arguments as is wants" | deactivator complete result process wasMeta | - "Set the deactivator literal for the fast path. - It will be patched to an exception handler" - deactivator := #fastdeactivator. - - "Quick check, if we are not in a meta-level, we are the first one here! - Chances are there are never meta-recursions. - Mark it and go FAST" - MetaOwner ifNil: [ "Take the ownership of the meta-level to call before" - MetaOwner := Processor activeProcess. - #handler beforeExecutionWithReceiver: self arguments: #( ). - - "Release it before forwarding the call" - MetaOwner := nil. - result := self - trapMethodwith: arg1 - with: arg2 - with: arg3 - with: arg4 - with: arg5. - - "Try to get it back and do the fast path if that's the case." - MetaOwner ifNil: [ - MetaOwner := Processor activeProcess. - result := #handler - afterExecutionWithReceiver: self - arguments: #( ) - returnValue: result. - MetaOwner := nil. - ^ result ]. - - "However, maybe another process took it. - In that case we will need to fall back to the slow case" - - "Set the deactivator literal for the slow path. - It will be patched to an exception handler" - deactivator := #slowdeactivator. - - - process := Processor activeProcess. - "Move to the meta level and call the after hooks. - Two after hooks are required. - One indicates the method is returning either because a normal return, - or a stack unwind due to exceptions or non-local returns - The other indicates we are returning normally with a value." - process shiftLevelUp. - wasMeta := true. - result := #handler - afterExecutionWithReceiver: self - arguments: #( ) - returnValue: result. - process shiftLevelDown. - wasMeta := false. - - "Mark the execution as complete to avoid double execution of the unwind handler" - complete := true. - ^ result ]. - - "If we are here, this is maybe a meta call. - Two possibilities: - - we are in the same thread of another meta call: we should just forward without instrumenting - - we are in another thread: increase the meta-level normally" - process := Processor activeProcess. - MetaOwner == Processor activeProcess ifTrue: [ + process isMeta ifTrue: [ ^ self trapMethodwith: arg1 with: arg2 @@ -869,78 +479,15 @@ MpMethodProxy class >> trapMethodwith: arg1 with: arg2 with: arg3 with: arg4 wit ^ result ] -{ #category : #'as yet unclassified' } +{ #category : 'as yet unclassified' } MpMethodProxy class >> trapMethodwith: arg1 with: arg2 with: arg3 with: arg4 with: arg5 with: arg6 [ "The unwind handler should be the first temp, the complete flag should be the second temp. Then this method is free to use as many extra temporaries and arguments as is wants" | deactivator complete result process wasMeta | - "Set the deactivator literal for the fast path. - It will be patched to an exception handler" - deactivator := #fastdeactivator. - - "Quick check, if we are not in a meta-level, we are the first one here! - Chances are there are never meta-recursions. - Mark it and go FAST" - MetaOwner ifNil: [ "Take the ownership of the meta-level to call before" - MetaOwner := Processor activeProcess. - #handler beforeExecutionWithReceiver: self arguments: #( ). - - "Release it before forwarding the call" - MetaOwner := nil. - result := self - trapMethodwith: arg1 - with: arg2 - with: arg3 - with: arg4 - with: arg5 - with: arg6. - - "Try to get it back and do the fast path if that's the case." - MetaOwner ifNil: [ - MetaOwner := Processor activeProcess. - result := #handler - afterExecutionWithReceiver: self - arguments: #( ) - returnValue: result. - MetaOwner := nil. - ^ result ]. - - "However, maybe another process took it. - In that case we will need to fall back to the slow case" - - "Set the deactivator literal for the slow path. - It will be patched to an exception handler" - deactivator := #slowdeactivator. - - - process := Processor activeProcess. - "Move to the meta level and call the after hooks. - Two after hooks are required. - One indicates the method is returning either because a normal return, - or a stack unwind due to exceptions or non-local returns - The other indicates we are returning normally with a value." - process shiftLevelUp. - wasMeta := true. - result := #handler - afterExecutionWithReceiver: self - arguments: #( ) - returnValue: result. - process shiftLevelDown. - wasMeta := false. - - "Mark the execution as complete to avoid double execution of the unwind handler" - complete := true. - ^ result ]. - - "If we are here, this is maybe a meta call. - Two possibilities: - - we are in the same thread of another meta call: we should just forward without instrumenting - - we are in another thread: increase the meta-level normally" - process := Processor activeProcess. - MetaOwner == Processor activeProcess ifTrue: [ + process isMeta ifTrue: [ ^ self trapMethodwith: arg1 with: arg2 @@ -991,79 +538,15 @@ MpMethodProxy class >> trapMethodwith: arg1 with: arg2 with: arg3 with: arg4 wit ^ result ] -{ #category : #'as yet unclassified' } +{ #category : 'as yet unclassified' } MpMethodProxy class >> trapMethodwith: arg1 with: arg2 with: arg3 with: arg4 with: arg5 with: arg6 with: arg7 [ "The unwind handler should be the first temp, the complete flag should be the second temp. Then this method is free to use as many extra temporaries and arguments as is wants" | deactivator complete result process wasMeta | - "Set the deactivator literal for the fast path. - It will be patched to an exception handler" - deactivator := #fastdeactivator. - - "Quick check, if we are not in a meta-level, we are the first one here! - Chances are there are never meta-recursions. - Mark it and go FAST" - MetaOwner ifNil: [ "Take the ownership of the meta-level to call before" - MetaOwner := Processor activeProcess. - #handler beforeExecutionWithReceiver: self arguments: #( ). - - "Release it before forwarding the call" - MetaOwner := nil. - result := self - trapMethodwith: arg1 - with: arg2 - with: arg3 - with: arg4 - with: arg5 - with: arg6 - with: arg7. - - "Try to get it back and do the fast path if that's the case." - MetaOwner ifNil: [ - MetaOwner := Processor activeProcess. - result := #handler - afterExecutionWithReceiver: self - arguments: #( ) - returnValue: result. - MetaOwner := nil. - ^ result ]. - - "However, maybe another process took it. - In that case we will need to fall back to the slow case" - - "Set the deactivator literal for the slow path. - It will be patched to an exception handler" - deactivator := #slowdeactivator. - - - process := Processor activeProcess. - "Move to the meta level and call the after hooks. - Two after hooks are required. - One indicates the method is returning either because a normal return, - or a stack unwind due to exceptions or non-local returns - The other indicates we are returning normally with a value." - process shiftLevelUp. - wasMeta := true. - result := #handler - afterExecutionWithReceiver: self - arguments: #( ) - returnValue: result. - process shiftLevelDown. - wasMeta := false. - - "Mark the execution as complete to avoid double execution of the unwind handler" - complete := true. - ^ result ]. - - "If we are here, this is maybe a meta call. - Two possibilities: - - we are in the same thread of another meta call: we should just forward without instrumenting - - we are in another thread: increase the meta-level normally" - process := Processor activeProcess. - MetaOwner == Processor activeProcess ifTrue: [ + process isMeta ifTrue: [ ^ self trapMethodwith: arg1 with: arg2 @@ -1116,80 +599,15 @@ MpMethodProxy class >> trapMethodwith: arg1 with: arg2 with: arg3 with: arg4 wit ^ result ] -{ #category : #'as yet unclassified' } +{ #category : 'as yet unclassified' } MpMethodProxy class >> trapMethodwith: arg1 with: arg2 with: arg3 with: arg4 with: arg5 with: arg6 with: arg7 with: arg8 [ "The unwind handler should be the first temp, the complete flag should be the second temp. Then this method is free to use as many extra temporaries and arguments as is wants" | deactivator complete result process wasMeta | - "Set the deactivator literal for the fast path. - It will be patched to an exception handler" - deactivator := #fastdeactivator. - - "Quick check, if we are not in a meta-level, we are the first one here! - Chances are there are never meta-recursions. - Mark it and go FAST" - MetaOwner ifNil: [ "Take the ownership of the meta-level to call before" - MetaOwner := Processor activeProcess. - #handler beforeExecutionWithReceiver: self arguments: #( ). - - "Release it before forwarding the call" - MetaOwner := nil. - result := self - trapMethodwith: arg1 - with: arg2 - with: arg3 - with: arg4 - with: arg5 - with: arg6 - with: arg7 - with: arg8. - - "Try to get it back and do the fast path if that's the case." - MetaOwner ifNil: [ - MetaOwner := Processor activeProcess. - result := #handler - afterExecutionWithReceiver: self - arguments: #( ) - returnValue: result. - MetaOwner := nil. - ^ result ]. - - "However, maybe another process took it. - In that case we will need to fall back to the slow case" - - "Set the deactivator literal for the slow path. - It will be patched to an exception handler" - deactivator := #slowdeactivator. - - - process := Processor activeProcess. - "Move to the meta level and call the after hooks. - Two after hooks are required. - One indicates the method is returning either because a normal return, - or a stack unwind due to exceptions or non-local returns - The other indicates we are returning normally with a value." - process shiftLevelUp. - wasMeta := true. - result := #handler - afterExecutionWithReceiver: self - arguments: #( ) - returnValue: result. - process shiftLevelDown. - wasMeta := false. - - "Mark the execution as complete to avoid double execution of the unwind handler" - complete := true. - ^ result ]. - - "If we are here, this is maybe a meta call. - Two possibilities: - - we are in the same thread of another meta call: we should just forward without instrumenting - - we are in another thread: increase the meta-level normally" - process := Processor activeProcess. - MetaOwner == Processor activeProcess ifTrue: [ + process isMeta ifTrue: [ ^ self trapMethodwith: arg1 with: arg2 @@ -1244,81 +662,15 @@ MpMethodProxy class >> trapMethodwith: arg1 with: arg2 with: arg3 with: arg4 wit ^ result ] -{ #category : #'as yet unclassified' } +{ #category : 'as yet unclassified' } MpMethodProxy class >> trapMethodwith: arg1 with: arg2 with: arg3 with: arg4 with: arg5 with: arg6 with: arg7 with: arg8 with: arg9 [ "The unwind handler should be the first temp, the complete flag should be the second temp. Then this method is free to use as many extra temporaries and arguments as is wants" | deactivator complete result process wasMeta | - "Set the deactivator literal for the fast path. - It will be patched to an exception handler" - deactivator := #fastdeactivator. - - "Quick check, if we are not in a meta-level, we are the first one here! - Chances are there are never meta-recursions. - Mark it and go FAST" - MetaOwner ifNil: [ "Take the ownership of the meta-level to call before" - MetaOwner := Processor activeProcess. - #handler beforeExecutionWithReceiver: self arguments: #( ). - - "Release it before forwarding the call" - MetaOwner := nil. - result := self - trapMethodwith: arg1 - with: arg2 - with: arg3 - with: arg4 - with: arg5 - with: arg6 - with: arg7 - with: arg8 - with: arg9. - - "Try to get it back and do the fast path if that's the case." - MetaOwner ifNil: [ - MetaOwner := Processor activeProcess. - result := #handler - afterExecutionWithReceiver: self - arguments: #( ) - returnValue: result. - MetaOwner := nil. - ^ result ]. - - "However, maybe another process took it. - In that case we will need to fall back to the slow case" - - "Set the deactivator literal for the slow path. - It will be patched to an exception handler" - deactivator := #slowdeactivator. - - - process := Processor activeProcess. - "Move to the meta level and call the after hooks. - Two after hooks are required. - One indicates the method is returning either because a normal return, - or a stack unwind due to exceptions or non-local returns - The other indicates we are returning normally with a value." - process shiftLevelUp. - wasMeta := true. - result := #handler - afterExecutionWithReceiver: self - arguments: #( ) - returnValue: result. - process shiftLevelDown. - wasMeta := false. - - "Mark the execution as complete to avoid double execution of the unwind handler" - complete := true. - ^ result ]. - - "If we are here, this is maybe a meta call. - Two possibilities: - - we are in the same thread of another meta call: we should just forward without instrumenting - - we are in another thread: increase the meta-level normally" - process := Processor activeProcess. - MetaOwner == Processor activeProcess ifTrue: [ + process isMeta ifTrue: [ ^ self trapMethodwith: arg1 with: arg2 @@ -1375,82 +727,15 @@ MpMethodProxy class >> trapMethodwith: arg1 with: arg2 with: arg3 with: arg4 wit ^ result ] -{ #category : #'as yet unclassified' } +{ #category : 'as yet unclassified' } MpMethodProxy class >> trapMethodwith: arg1 with: arg2 with: arg3 with: arg4 with: arg5 with: arg6 with: arg7 with: arg8 with: arg9 with: arg10 [ "The unwind handler should be the first temp, the complete flag should be the second temp. Then this method is free to use as many extra temporaries and arguments as is wants" | deactivator complete result process wasMeta | - "Set the deactivator literal for the fast path. - It will be patched to an exception handler" - deactivator := #fastdeactivator. - - "Quick check, if we are not in a meta-level, we are the first one here! - Chances are there are never meta-recursions. - Mark it and go FAST" - MetaOwner ifNil: [ "Take the ownership of the meta-level to call before" - MetaOwner := Processor activeProcess. - #handler beforeExecutionWithReceiver: self arguments: #( ). - - "Release it before forwarding the call" - MetaOwner := nil. - result := self - trapMethodwith: arg1 - with: arg2 - with: arg3 - with: arg4 - with: arg5 - with: arg6 - with: arg7 - with: arg8 - with: arg9 - with: arg10. - - "Try to get it back and do the fast path if that's the case." - MetaOwner ifNil: [ - MetaOwner := Processor activeProcess. - result := #handler - afterExecutionWithReceiver: self - arguments: #( ) - returnValue: result. - MetaOwner := nil. - ^ result ]. - - "However, maybe another process took it. - In that case we will need to fall back to the slow case" - - "Set the deactivator literal for the slow path. - It will be patched to an exception handler" - deactivator := #slowdeactivator. - - - process := Processor activeProcess. - "Move to the meta level and call the after hooks. - Two after hooks are required. - One indicates the method is returning either because a normal return, - or a stack unwind due to exceptions or non-local returns - The other indicates we are returning normally with a value." - process shiftLevelUp. - wasMeta := true. - result := #handler - afterExecutionWithReceiver: self - arguments: #( ) - returnValue: result. - process shiftLevelDown. - wasMeta := false. - - "Mark the execution as complete to avoid double execution of the unwind handler" - complete := true. - ^ result ]. - - "If we are here, this is maybe a meta call. - Two possibilities: - - we are in the same thread of another meta call: we should just forward without instrumenting - - we are in another thread: increase the meta-level normally" - process := Processor activeProcess. - MetaOwner == Processor activeProcess ifTrue: [ + process isMeta ifTrue: [ ^ self trapMethodwith: arg1 with: arg2 @@ -1509,83 +794,15 @@ MpMethodProxy class >> trapMethodwith: arg1 with: arg2 with: arg3 with: arg4 wit ^ result ] -{ #category : #'as yet unclassified' } +{ #category : 'as yet unclassified' } MpMethodProxy class >> trapMethodwith: arg1 with: arg2 with: arg3 with: arg4 with: arg5 with: arg6 with: arg7 with: arg8 with: arg9 with: arg10 with: arg11 [ "The unwind handler should be the first temp, the complete flag should be the second temp. Then this method is free to use as many extra temporaries and arguments as is wants" | deactivator complete result process wasMeta | - "Set the deactivator literal for the fast path. - It will be patched to an exception handler" - deactivator := #fastdeactivator. - - "Quick check, if we are not in a meta-level, we are the first one here! - Chances are there are never meta-recursions. - Mark it and go FAST" - MetaOwner ifNil: [ "Take the ownership of the meta-level to call before" - MetaOwner := Processor activeProcess. - #handler beforeExecutionWithReceiver: self arguments: #( ). - - "Release it before forwarding the call" - MetaOwner := nil. - result := self - trapMethodwith: arg1 - with: arg2 - with: arg3 - with: arg4 - with: arg5 - with: arg6 - with: arg7 - with: arg8 - with: arg9 - with: arg10 - with: arg11. - - "Try to get it back and do the fast path if that's the case." - MetaOwner ifNil: [ - MetaOwner := Processor activeProcess. - result := #handler - afterExecutionWithReceiver: self - arguments: #( ) - returnValue: result. - MetaOwner := nil. - ^ result ]. - - "However, maybe another process took it. - In that case we will need to fall back to the slow case" - - "Set the deactivator literal for the slow path. - It will be patched to an exception handler" - deactivator := #slowdeactivator. - - - process := Processor activeProcess. - "Move to the meta level and call the after hooks. - Two after hooks are required. - One indicates the method is returning either because a normal return, - or a stack unwind due to exceptions or non-local returns - The other indicates we are returning normally with a value." - process shiftLevelUp. - wasMeta := true. - result := #handler - afterExecutionWithReceiver: self - arguments: #( ) - returnValue: result. - process shiftLevelDown. - wasMeta := false. - - "Mark the execution as complete to avoid double execution of the unwind handler" - complete := true. - ^ result ]. - - "If we are here, this is maybe a meta call. - Two possibilities: - - we are in the same thread of another meta call: we should just forward without instrumenting - - we are in another thread: increase the meta-level normally" - process := Processor activeProcess. - MetaOwner == Processor activeProcess ifTrue: [ + process isMeta ifTrue: [ ^ self trapMethodwith: arg1 with: arg2 @@ -1646,84 +863,15 @@ MpMethodProxy class >> trapMethodwith: arg1 with: arg2 with: arg3 with: arg4 wit ^ result ] -{ #category : #'as yet unclassified' } +{ #category : 'as yet unclassified' } MpMethodProxy class >> trapMethodwith: arg1 with: arg2 with: arg3 with: arg4 with: arg5 with: arg6 with: arg7 with: arg8 with: arg9 with: arg10 with: arg11 with: arg12 [ "The unwind handler should be the first temp, the complete flag should be the second temp. Then this method is free to use as many extra temporaries and arguments as is wants" | deactivator complete result process wasMeta | - "Set the deactivator literal for the fast path. - It will be patched to an exception handler" - deactivator := #fastdeactivator. - - "Quick check, if we are not in a meta-level, we are the first one here! - Chances are there are never meta-recursions. - Mark it and go FAST" - MetaOwner ifNil: [ "Take the ownership of the meta-level to call before" - MetaOwner := Processor activeProcess. - #handler beforeExecutionWithReceiver: self arguments: #( ). - - "Release it before forwarding the call" - MetaOwner := nil. - result := self - trapMethodwith: arg1 - with: arg2 - with: arg3 - with: arg4 - with: arg5 - with: arg6 - with: arg7 - with: arg8 - with: arg9 - with: arg10 - with: arg11 - with: arg12. - - "Try to get it back and do the fast path if that's the case." - MetaOwner ifNil: [ - MetaOwner := Processor activeProcess. - result := #handler - afterExecutionWithReceiver: self - arguments: #( ) - returnValue: result. - MetaOwner := nil. - ^ result ]. - - "However, maybe another process took it. - In that case we will need to fall back to the slow case" - - "Set the deactivator literal for the slow path. - It will be patched to an exception handler" - deactivator := #slowdeactivator. - - - process := Processor activeProcess. - "Move to the meta level and call the after hooks. - Two after hooks are required. - One indicates the method is returning either because a normal return, - or a stack unwind due to exceptions or non-local returns - The other indicates we are returning normally with a value." - process shiftLevelUp. - wasMeta := true. - result := #handler - afterExecutionWithReceiver: self - arguments: #( ) - returnValue: result. - process shiftLevelDown. - wasMeta := false. - - "Mark the execution as complete to avoid double execution of the unwind handler" - complete := true. - ^ result ]. - - "If we are here, this is maybe a meta call. - Two possibilities: - - we are in the same thread of another meta call: we should just forward without instrumenting - - we are in another thread: increase the meta-level normally" - process := Processor activeProcess. - MetaOwner == Processor activeProcess ifTrue: [ + process isMeta ifTrue: [ ^ self trapMethodwith: arg1 with: arg2 @@ -1786,85 +934,15 @@ MpMethodProxy class >> trapMethodwith: arg1 with: arg2 with: arg3 with: arg4 wit ^ result ] -{ #category : #'as yet unclassified' } +{ #category : 'as yet unclassified' } MpMethodProxy class >> trapMethodwith: arg1 with: arg2 with: arg3 with: arg4 with: arg5 with: arg6 with: arg7 with: arg8 with: arg9 with: arg10 with: arg11 with: arg12 with: arg13 [ "The unwind handler should be the first temp, the complete flag should be the second temp. Then this method is free to use as many extra temporaries and arguments as is wants" | deactivator complete result process wasMeta | - "Set the deactivator literal for the fast path. - It will be patched to an exception handler" - deactivator := #fastdeactivator. - - "Quick check, if we are not in a meta-level, we are the first one here! - Chances are there are never meta-recursions. - Mark it and go FAST" - MetaOwner ifNil: [ "Take the ownership of the meta-level to call before" - MetaOwner := Processor activeProcess. - #handler beforeExecutionWithReceiver: self arguments: #( ). - - "Release it before forwarding the call" - MetaOwner := nil. - result := self - trapMethodwith: arg1 - with: arg2 - with: arg3 - with: arg4 - with: arg5 - with: arg6 - with: arg7 - with: arg8 - with: arg9 - with: arg10 - with: arg11 - with: arg12 - with: arg13. - - "Try to get it back and do the fast path if that's the case." - MetaOwner ifNil: [ - MetaOwner := Processor activeProcess. - result := #handler - afterExecutionWithReceiver: self - arguments: #( ) - returnValue: result. - MetaOwner := nil. - ^ result ]. - - "However, maybe another process took it. - In that case we will need to fall back to the slow case" - - "Set the deactivator literal for the slow path. - It will be patched to an exception handler" - deactivator := #slowdeactivator. - - - process := Processor activeProcess. - "Move to the meta level and call the after hooks. - Two after hooks are required. - One indicates the method is returning either because a normal return, - or a stack unwind due to exceptions or non-local returns - The other indicates we are returning normally with a value." - process shiftLevelUp. - wasMeta := true. - result := #handler - afterExecutionWithReceiver: self - arguments: #( ) - returnValue: result. - process shiftLevelDown. - wasMeta := false. - - "Mark the execution as complete to avoid double execution of the unwind handler" - complete := true. - ^ result ]. - - "If we are here, this is maybe a meta call. - Two possibilities: - - we are in the same thread of another meta call: we should just forward without instrumenting - - we are in another thread: increase the meta-level normally" - process := Processor activeProcess. - MetaOwner == Processor activeProcess ifTrue: [ + process isMeta ifTrue: [ ^ self trapMethodwith: arg1 with: arg2 @@ -1929,86 +1007,15 @@ MpMethodProxy class >> trapMethodwith: arg1 with: arg2 with: arg3 with: arg4 wit ^ result ] -{ #category : #'as yet unclassified' } +{ #category : 'as yet unclassified' } MpMethodProxy class >> trapMethodwith: arg1 with: arg2 with: arg3 with: arg4 with: arg5 with: arg6 with: arg7 with: arg8 with: arg9 with: arg10 with: arg11 with: arg12 with: arg13 with: arg14 [ "The unwind handler should be the first temp, the complete flag should be the second temp. Then this method is free to use as many extra temporaries and arguments as is wants" | deactivator complete result process wasMeta | - "Set the deactivator literal for the fast path. - It will be patched to an exception handler" - deactivator := #fastdeactivator. - - "Quick check, if we are not in a meta-level, we are the first one here! - Chances are there are never meta-recursions. - Mark it and go FAST" - MetaOwner ifNil: [ "Take the ownership of the meta-level to call before" - MetaOwner := Processor activeProcess. - #handler beforeExecutionWithReceiver: self arguments: #( ). - - "Release it before forwarding the call" - MetaOwner := nil. - result := self - trapMethodwith: arg1 - with: arg2 - with: arg3 - with: arg4 - with: arg5 - with: arg6 - with: arg7 - with: arg8 - with: arg9 - with: arg10 - with: arg11 - with: arg12 - with: arg13 - with: arg14. - - "Try to get it back and do the fast path if that's the case." - MetaOwner ifNil: [ - MetaOwner := Processor activeProcess. - result := #handler - afterExecutionWithReceiver: self - arguments: #( ) - returnValue: result. - MetaOwner := nil. - ^ result ]. - - "However, maybe another process took it. - In that case we will need to fall back to the slow case" - - "Set the deactivator literal for the slow path. - It will be patched to an exception handler" - deactivator := #slowdeactivator. - - - process := Processor activeProcess. - "Move to the meta level and call the after hooks. - Two after hooks are required. - One indicates the method is returning either because a normal return, - or a stack unwind due to exceptions or non-local returns - The other indicates we are returning normally with a value." - process shiftLevelUp. - wasMeta := true. - result := #handler - afterExecutionWithReceiver: self - arguments: #( ) - returnValue: result. - process shiftLevelDown. - wasMeta := false. - - "Mark the execution as complete to avoid double execution of the unwind handler" - complete := true. - ^ result ]. - - "If we are here, this is maybe a meta call. - Two possibilities: - - we are in the same thread of another meta call: we should just forward without instrumenting - - we are in another thread: increase the meta-level normally" - process := Processor activeProcess. - MetaOwner == Processor activeProcess ifTrue: [ + process isMeta ifTrue: [ ^ self trapMethodwith: arg1 with: arg2 @@ -2075,87 +1082,15 @@ MpMethodProxy class >> trapMethodwith: arg1 with: arg2 with: arg3 with: arg4 wit ^ result ] -{ #category : #'as yet unclassified' } +{ #category : 'as yet unclassified' } MpMethodProxy class >> trapMethodwith: arg1 with: arg2 with: arg3 with: arg4 with: arg5 with: arg6 with: arg7 with: arg8 with: arg9 with: arg10 with: arg11 with: arg12 with: arg13 with: arg14 with: arg15 [ "The unwind handler should be the first temp, the complete flag should be the second temp. Then this method is free to use as many extra temporaries and arguments as is wants" | deactivator complete result process wasMeta | - "Set the deactivator literal for the fast path. - It will be patched to an exception handler" - deactivator := #fastdeactivator. - - "Quick check, if we are not in a meta-level, we are the first one here! - Chances are there are never meta-recursions. - Mark it and go FAST" - MetaOwner ifNil: [ "Take the ownership of the meta-level to call before" - MetaOwner := Processor activeProcess. - #handler beforeExecutionWithReceiver: self arguments: #( ). - - "Release it before forwarding the call" - MetaOwner := nil. - result := self - trapMethodwith: arg1 - with: arg2 - with: arg3 - with: arg4 - with: arg5 - with: arg6 - with: arg7 - with: arg8 - with: arg9 - with: arg10 - with: arg11 - with: arg12 - with: arg13 - with: arg14 - with: arg15. - - "Try to get it back and do the fast path if that's the case." - MetaOwner ifNil: [ - MetaOwner := Processor activeProcess. - result := #handler - afterExecutionWithReceiver: self - arguments: #( ) - returnValue: result. - MetaOwner := nil. - ^ result ]. - - "However, maybe another process took it. - In that case we will need to fall back to the slow case" - - "Set the deactivator literal for the slow path. - It will be patched to an exception handler" - deactivator := #slowdeactivator. - - - process := Processor activeProcess. - "Move to the meta level and call the after hooks. - Two after hooks are required. - One indicates the method is returning either because a normal return, - or a stack unwind due to exceptions or non-local returns - The other indicates we are returning normally with a value." - process shiftLevelUp. - wasMeta := true. - result := #handler - afterExecutionWithReceiver: self - arguments: #( ) - returnValue: result. - process shiftLevelDown. - wasMeta := false. - - "Mark the execution as complete to avoid double execution of the unwind handler" - complete := true. - ^ result ]. - - "If we are here, this is maybe a meta call. - Two possibilities: - - we are in the same thread of another meta call: we should just forward without instrumenting - - we are in another thread: increase the meta-level normally" - process := Processor activeProcess. - MetaOwner == Processor activeProcess ifTrue: [ + process isMeta ifTrue: [ ^ self trapMethodwith: arg1 with: arg2 @@ -2224,63 +1159,62 @@ MpMethodProxy class >> trapMethodwith: arg1 with: arg2 with: arg3 with: arg4 wit ^ result ] -{ #category : #accessing } +{ #category : 'installation' } +MpMethodProxy >> disableInstrumentation [ + + self class disableInstrumentation +] + +{ #category : 'installation' } +MpMethodProxy >> enableInstrumentation [ + + self class enableInstrumentation +] + +{ #category : 'accessing' } MpMethodProxy >> handler [ ^ handler ] -{ #category : #accessing } +{ #category : 'accessing' } MpMethodProxy >> handler: anObject [ handler := anObject ] -{ #category : #installation } +{ #category : 'installation' } MpMethodProxy >> install [ - | slowdeactivator newTrap index trapSelector fastdeactivator | + | slowdeactivator newTrap index trapSelector | + + "Disable instrumentation during installation to make it safe. + We don't want to call instrumented methods when installing the instrumentation" + self disableInstrumentation. + thisProcess runInMetaLevel: [ - MetaOwner := Processor activeProcess. - [ (proxifiedMethod hasPragmaNamed: #noInstrumentation) ifTrue: [ ^ MpCannotInstall signalWith: self ]. - slowdeactivator := [ "Execution handler for the slow path. An exception or a non local return happened during proxy execution" - | wasMeta trapContext | - "Jump to the meta level (to avoid meta-recursions) to observe if the handler was in a meta level, marked by the wasMeta flag. - During the meta-jump call the handler to tell there was an unwind. - " - thisProcess shiftLevelUp. - trapContext := thisContext findContextSuchThat: [ - :ctx | ctx isUnwindContext ]. - wasMeta := trapContext tempNamed: 'wasMeta'. - handler - aboutToReturnWithReceiver: trapContext receiver - arguments: trapContext arguments. - thisProcess shiftLevelDown. - - "If the handler was in a meta-state (i.e., the exception or non-local return happened in the handler), shift it back to the base level before returning. - Otherwise, we were already in the base level and we need to do nothing!" - wasMeta ifTrue: [ thisProcess shiftLevelDown ] ]. - - fastdeactivator := [ | trapContext | - "If we fall in here, we were in the fast path. - This means we were the owners of the meta level when calling before or after. - If that it the case, release ownership. - - But! this could have been stolen when forwarding. - If that is the case, leave the ownership to the othe thread - " + slowdeactivator := [ + "Execution handler for the slow path. An exception or a non local return happened during proxy execution" + | wasMeta trapContext | + "Jump to the meta level (to avoid meta-recursions) to observe if the handler was in a meta level, marked by the wasMeta flag. + During the meta-jump call the handler to tell there was an unwind." thisProcess shiftLevelUp. - trapContext := thisContext findContextSuchThat: [ - :ctx | ctx isUnwindContext ]. - handler + trapContext := thisContext findNextUnwindContextUpTo: nil. + wasMeta := trapContext tempNamed: 'wasMeta'. + handler aboutToReturnWithReceiver: trapContext receiver - arguments: trapContext arguments. - thisProcess shiftLevelDown. - MetaOwner == Processor activeProcess - ifTrue: [ MetaOwner := nil ] ]. + arguments: trapContext arguments. + thisProcess shiftLevelDown. + + "If the handler was in a meta-state (i.e., the exception or non-local return happened in the handler), shift it back to the base level before returning. + Otherwise, we were already in the base level and we need to do nothing!" + wasMeta ifTrue: [ thisProcess shiftLevelDown ]. + + "Mark execution as complete to avoid double execution of the handler." + trapContext tempNamed: 'complete' put: true. ]. newTrap := self prototypeTrapMethod copy. trapSelector := newTrap selector. @@ -2295,17 +1229,13 @@ MpMethodProxy >> install [ index := newTrap literals indexOf: #handler. newTrap literalAt: index put: handler. - index := newTrap literals indexOf: #fastdeactivator. - newTrap literalAt: index put: fastdeactivator. - index := newTrap literals indexOf: #slowdeactivator. newTrap literalAt: index put: slowdeactivator. - "It could happen that a proxy wraps a proxy. - Remember the object that was installed at this moment. + "It could happen that a proxy wraps a proxy. Remember the object that was installed at this moment. This is the object to restore during uninstall" - wrappedMethod := proxifiedMethod methodClass methodDict at: - proxifiedMethod selector. + wrappedMethod := proxifiedMethod methodClass + methodDict at: proxifiedMethod selector. proxifiedMethod methodClass methodDict at: hiddenSelector @@ -2314,10 +1244,10 @@ MpMethodProxy >> install [ at: proxifiedMethod selector put: newTrap. - trapMethod := newTrap ] ensure: [ MetaOwner := nil ] ] + trapMethod := newTrap ] ] -{ #category : #testing } +{ #category : 'testing' } MpMethodProxy >> isInstalled [ trapMethod ifNil: [ ^ false ]. @@ -2325,13 +1255,13 @@ MpMethodProxy >> isInstalled [ ^ proxifiedMethod methodClass >> proxifiedMethod selector == trapMethod ] -{ #category : #accessing } +{ #category : 'accessing' } MpMethodProxy >> methodClass [ ^ proxifiedMethod methodClass ] -{ #category : #installation } +{ #category : 'installation' } MpMethodProxy >> prototypeTrapMethod [ ^ self class class methods detect: [ :m | @@ -2339,33 +1269,36 @@ MpMethodProxy >> prototypeTrapMethod [ m selector beginsWith: 'trap' ] ] ] -{ #category : #accessing } +{ #category : 'accessing' } MpMethodProxy >> proxifiedMethod [ ^ proxifiedMethod ] -{ #category : #accessing } +{ #category : 'accessing' } MpMethodProxy >> proxyMethod: anObject [ proxifiedMethod := anObject ] -{ #category : #accessing } +{ #category : 'accessing' } MpMethodProxy >> selector [ ^ proxifiedMethod selector ] -{ #category : #accessing } +{ #category : 'accessing' } MpMethodProxy >> trapMethod [ ^ trapMethod ] -{ #category : #installation } +{ #category : 'installation' } MpMethodProxy >> uninstall [ + "Disable instrumentation during installation to make it safe." + self disableInstrumentation. + self isInstalled ifFalse: [ ^ self ]. thisProcess runInMetaLevel: [ diff --git a/src/MethodProxies/package.st b/src/MethodProxies/package.st index 7dc8373..4924b6a 100644 --- a/src/MethodProxies/package.st +++ b/src/MethodProxies/package.st @@ -1 +1 @@ -Package { #name : #MethodProxies } +Package { #name : 'MethodProxies' }