77import org .jruby .RubyClass ;
88import org .jruby .RubyObject ;
99import org .jruby .RubySymbol ;
10+ import org .jruby .RubyThread ;
1011import org .jruby .anno .JRubyMethod ;
1112import org .jruby .exceptions .RaiseException ;
1213import org .jruby .runtime .Block ;
@@ -77,60 +78,70 @@ public IRubyObject initialize(ThreadContext context, IRubyObject[] _events, fina
7778
7879 if (!block .isGiven ()) throw argumentError (context , "must be called with a block" );
7980
80- final ThreadContext threadToTrace = context ;
81- hook = new EventHook () {
82- @ Override
83- public void event (ThreadContext context , RubyEvent event , String file , int line , String name , IRubyObject type ) {
84- if (!enabled || threadToTrace != context || context .isWithinTrace ()) return ;
81+ hook = new TracePointEventHook (eventSet , block );
82+
83+ return context .nil ;
84+ }
8585
86- int savedCallInfo = resetCallInfo (context );
86+ private class TracePointEventHook extends EventHook {
87+ protected ThreadContext threadToTrace ;
88+ private final Block block ;
89+ private final EnumSet <RubyEvent > eventSet ;
8790
88- synchronized (this ) {
89- inside = true ;
91+ public TracePointEventHook (EnumSet <RubyEvent > eventSet , Block block ) {
92+ this .block = block ;
93+ this .eventSet = eventSet ;
94+ }
9095
91- if (file == null ) file = "(ruby)" ;
92- if (type == null ) type = context .fals ;
96+ @ Override
97+ public void event (ThreadContext context , RubyEvent event , String file , int line , String name , IRubyObject type ) {
98+ if (!enabled || (threadToTrace != null && threadToTrace != context ) || context .isWithinTrace ()) return ;
9399
94- IRubyObject binding ;
95- if (event == RubyEvent .THREAD_BEGIN || event == RubyEvent .THREAD_END ) {
96- binding = context .nil ;
97- } else {
98- binding = RubyBinding .newBinding (context .runtime , context .currentBinding ());
99- }
100+ int savedCallInfo = resetCallInfo (context );
100101
101- context .preTrace ();
102+ synchronized (this ) {
103+ inside = true ;
102104
103- // FIXME: get return value
104- update ( event . getName (), file , line , name , type , context . getErrorInfo (), context .nil , binding ) ;
105+ if ( file == null ) file = "(ruby)" ;
106+ if ( type == null ) type = context .fals ;
105107
106- try {
107- block .yieldSpecific (context , TracePoint .this );
108- } finally {
109- update (null , null , line , null , context .nil , context .nil , context .nil , context .nil );
110- context .postTrace ();
111- inside = false ;
112- context .callInfo = savedCallInfo ;
113- }
108+ IRubyObject binding ;
109+ if (event == RubyEvent .THREAD_BEGIN || event == RubyEvent .THREAD_END ) {
110+ binding = context .nil ;
111+ } else {
112+ binding = RubyBinding .newBinding (context .runtime , context .currentBinding ());
114113 }
115- }
116114
117- @ Override
118- public void eventHandler (ThreadContext context , String eventName , String file , int line , String name , IRubyObject type ) {
119- event (context , RubyEvent .fromName (eventName ), file , line , name , type );
120- }
115+ context .preTrace ();
121116
122- @ Override
123- public boolean isInterestedInEvent (RubyEvent event ) {
124- return eventSet .contains (event );
125- }
117+ // FIXME: get return value
118+ update (event .getName (), file , line , name , type , context .getErrorInfo (), context .nil , binding );
126119
127- @ Override
128- public EnumSet <RubyEvent > eventSet () {
129- return eventSet ;
120+ try {
121+ block .yieldSpecific (context , TracePoint .this );
122+ } finally {
123+ update (null , null , line , null , context .nil , context .nil , context .nil , context .nil );
124+ context .postTrace ();
125+ inside = false ;
126+ context .callInfo = savedCallInfo ;
127+ }
130128 }
131- };
132-
133- return context .nil ;
129+ }
130+
131+ @ Override
132+ public void eventHandler (ThreadContext context , String eventName , String file , int line , String name , IRubyObject type ) {
133+ event (context , RubyEvent .fromName (eventName ), file , line , name , type );
134+ }
135+
136+ @ Override
137+ public boolean isInterestedInEvent (RubyEvent event ) {
138+ return eventSet .contains (event );
139+ }
140+
141+ @ Override
142+ public EnumSet <RubyEvent > eventSet () {
143+ return eventSet ;
144+ }
134145 }
135146
136147 @ JRubyMethod
@@ -149,12 +160,48 @@ public IRubyObject defined_class(ThreadContext context) {
149160
150161 @ JRubyMethod
151162 public IRubyObject disable (ThreadContext context , Block block ) {
152- return doToggle (context , block , false );
163+ return doToggle (context , null , block , false );
153164 }
154-
165+
155166 @ JRubyMethod
156167 public IRubyObject enable (ThreadContext context , Block block ) {
157- return doToggle (context , block , true );
168+ return doToggle (context , null , block , true );
169+ }
170+
171+ @ JRubyMethod
172+ public IRubyObject enable (ThreadContext context , IRubyObject target , Block block ) {
173+ // TODO: implement target
174+ if (!target .isNil ()) {
175+ context .runtime .getWarnings ().warning ("target argument to TracePoint.enable is unsupported" );
176+ }
177+
178+ return doToggle (context , null , block , true );
179+ }
180+
181+ @ JRubyMethod
182+ public IRubyObject enable (ThreadContext context , IRubyObject target , IRubyObject targetLine , Block block ) {
183+ // TODO: implement target, target_line
184+ if (!target .isNil () || !targetLine .isNil ()) {
185+ context .runtime .getWarnings ().warning ("target and target_line arguments to TracePoint.enable are unsupported" );
186+ }
187+
188+ return doToggle (context , null , block , true );
189+ }
190+
191+ @ JRubyMethod
192+ public IRubyObject enable (ThreadContext context , IRubyObject target , IRubyObject targetLine , IRubyObject _targetThread , Block block ) {
193+ // TODO: implement target, target_line
194+ if (!target .isNil () || !targetLine .isNil ()) {
195+ context .runtime .getWarnings ().warning ("target and target_line arguments to TracePoint.enable are unsupported" );
196+ }
197+
198+ RubyThread targetThread = null ;
199+ if (_targetThread != asSymbol (context , "default" )) {
200+ if (!(_targetThread instanceof RubyThread th )) throw argumentError (context , "target must be a Thread" );
201+ targetThread = th ;
202+ }
203+
204+ return doToggle (context , targetThread , block , true );
158205 }
159206
160207 @ JRubyMethod (name = "enabled?" )
@@ -244,32 +291,36 @@ private void update(String eventName, String file, int line, String name, IRubyO
244291 this .binding = binding ;
245292 }
246293
247- private synchronized IRubyObject doToggle (ThreadContext context , Block block , boolean toggle ) {
294+ private synchronized IRubyObject doToggle (ThreadContext context , RubyThread _targetThread , Block block , boolean toggle ) {
248295 if (block .isGiven ()) {
249296 boolean old = enabled ;
250297 try {
251- updateEnabled (context , toggle );
298+ updateEnabled (context , _targetThread , toggle );
252299
253300 return block .yieldSpecific (context );
254301 } finally {
255- updateEnabled (context , old );
302+ updateEnabled (context , _targetThread , old );
256303 }
257304 }
258305
259306 IRubyObject old = asBoolean (context , enabled );
260- updateEnabled (context , toggle );
307+ updateEnabled (context , _targetThread , toggle );
261308
262309 return old ;
263310 }
264311
265- public void updateEnabled (ThreadContext context , boolean toggle ) {
312+ public void updateEnabled (ThreadContext context , RubyThread targetThread , boolean toggle ) {
266313 if (toggle == enabled ) return ; // don't re-add or re-remove hook
267314
268315 enabled = toggle ;
269316
270317 if (toggle ) {
318+ if (targetThread != null && !targetThread .isNil ()) {
319+ hook .threadToTrace = targetThread .getContext ();
320+ }
271321 context .traceEvents .addEventHook (context , hook );
272322 } else {
323+ hook .threadToTrace = null ;
273324 context .traceEvents .removeEventHook (hook );
274325 }
275326 }
@@ -282,7 +333,7 @@ private static JavaSites.TracePointSites sites(ThreadContext context) {
282333 return context .sites .TracePoint ;
283334 }
284335
285- private EventHook hook ;
336+ private TracePointEventHook hook ;
286337 private volatile boolean enabled = false ;
287338 private String eventName ;
288339 private String file ;
0 commit comments