Description

None

Activity

Brad WoodMarch 14, 2025 at 5:24 PM

Yeah, I still don’t really understand what code was being used or why, but the simple matter is this; if you haven’t called the constructor, then there is no instance, and there is no this scope! It’s literally impossible to access instance data with no instance. It sounds to me like our modules are incorrect and need to be updated. If they want to just access static data, then loading a box class and not calling the constructor is fine, but if we want to access instance data on a box class, then we have to instantiate it. There’s no way around this.

NOW, if you want to create an instance of a box class and you want to instantiate it (which runs the pseudo-constructor) but you don’t want to run the actual init() method, then this is easy. Just copy this code from the createObject() BIF

return result.invokeConstructor( context, Key.noInit ).unWrapBoxLangClass();

This will give you a proper instance without calling the init().

 

As far as changing the behavior of the unwrap method, that’s a no-go. That method is designed to be safe to call regardless of what’s in the dynamic object. If it’s a box class instance, then it will kick in, but it will ignore all other uses cases. We use it in several places to ensure box class instances are never wrapped in a DO, but all other class instances are allowed to pass through untouched.

Jon ClausenMarch 14, 2025 at 2:53 PM

how do you go about getting an iclassrunnable wrapped in a dynamic object

CLASS_LOCATOR.load( context, fqn, ClassLocator.BX_PREFIX, true, context.getCurrentImports() )


This was kind of a chicken/egg issue. In bx-orm we were loading the event listener class without invoking the constructor, so unWrap called from the dynamic object was never castable to an instance of IClassRunnable - which then meant that we we couldn’t inspect whether the method was available using:

( ( IClassRunnable ) globalListener.unWrapBoxLangClass() ).getThisScope().containsKey( eventType )


So, in effect, this is a non-issue for the subject above. I would suggest having unwrapBoxLangClass throw an error if the target instance is null or is not castable to to a runnable or interface, rather than returning back the dynamic object. I probably would have been able to troubleshoot the constructor issue a lot more easily. Since this is only relevant to those developing BoxLang modules, though, I’ll create a separate issue for that and close this one out.

Jon ClausenMarch 14, 2025 at 4:17 AM

Either way, I would maybe look at checking the this scope in addition to the static scope before choosing which invocation to fire

Jon ClausenMarch 14, 2025 at 4:16 AM

Maybe it is the way the DynamicObject is being created then. ( e.g - If the constructor is invoked outside of the dynamic object, thus before the DO is created, the instance is null )

I ran in to this with bx-orm and the usage of the event handler object - which can be either a Java or a BoxLang class.

Brad WoodMarch 14, 2025 at 3:58 AM
Edited

Looking at the code, I can’t see the issue. We have this here in the dereferenceAndInvoke() method

// If this dynamic object represnts a Box Class (not an instance), then invoke static if ( IClassRunnable.class.isAssignableFrom( targetClass ) && targetInstance == null ) { return BoxClassSupport.dereferenceAndInvokeStatic( this, context, name, positionalArguments, safe ); } return DynamicInteropService.dereferenceAndInvoke( this, this.targetClass, this.targetInstance, context, name, positionalArguments, safe );

you can see we’re specifically checking if targetINtsance is null, which means there is no instance and it must be a static class reference, so that all seems correct. We then delegate to the generic method in the dynamic interop service, which runs this code at the very top of the method

// If the object is referencable, allow it to handle the dereference itself if ( IReferenceable.class.isAssignableFrom( targetClass ) && targetInstance != null && targetInstance instanceof IReferenceable ref ) { return ref.dereferenceAndInvoke( context, name, positionalArguments, safe ); }

This would recognize that the iclassrunnable instance is an ireferenceable, and SHOULD let it proceed as normal. I’ll need some code that shows the issue. All the proper handling seems to be in order.

Pinned fields
Click on the next to a field label to start pinning.

Details

Assignee

Brad Wood

Reporter

Jon Clausen

Fix versions

Priority

Major

Sentry