Calculated value depending on preceeding elements

Hi, I am working on a language to represent JVM class files (https://github.com/ftomassetti/MpsByteCode). More specifically I am working on representing the index of the ConstantPoolElements. The index of the each element of the ConstantPool is the 1-based position of the element in the pool BUT some kinds of elements get two spaces, so they count double. For example:

ConstantPool:

  • 1 A
  • 2 B
  • 3 C (counts double)
  • 5 D
  • 6 E (counts double)
  • 8 F

The other thing to consider is that theConstantPool can contain thousands of elements.

I would rather avoid having the index stored in a property because it is a calculated value. I thought about using memoization and store that value using the putUserObject/getUserObject system, however I would need to clear that cache when the list changes. I tried to attach a listener to the whole ClassPool. I have two problems with this:

  • when can I attach the listener? For now I am using a trick: I add a read only element in the editor of the ClassPool and in the block calculating the string to return I invoke the function to attach the listener, I then just return an empty string. In this way I know that every time the user edit the model there is the listener in place
  • however if I do that I need to somehow refresh the cell showing the index of each component. I was thinking to use: editorContext.getEditorComponent().findNodeCell(<class pool element>).requestRelayout()

This solution seems way to complex than needed. Do you have better ideas? Something I am missing?

By the way implementing this solution I get an exception rather obscure to me:

jetbrains.mps.classloading.ModuleClassLoader$ModuleClassLoaderIsDisposedException: ClassLoader of the module 'me.tomassetti.bytecode [language]' is disposed and not operable!

5 comments

Hello

As we have struggeled our fair share with listeners in our project maybe I can help.

We get the same error message when multiple instances of the listener are running. We start our listeners via a project plugin solution, so the listeners get started every time you open the project and of course every time you build the plugin solution. 

I did not really understand how and when you start your listeners, but I assume you start them serveral times while using/bulidng your language.

You might also want to look at ProjectHelper.executeCommand ({=> DO WHAT THE LISTENER DOES}), as there can be some problems with model access too.

0

Thankyou Jochen! I have never used ProjectHelper.executeCommand, I will look into it.

0

Hi Federico, Jochen!

As for the classloader disposed errors, it's most likely because you attach listeners but don't detach them. The thing is the class of your listener lives in the classloader of your language module. MPS routinely reloads classes of your languages, for example when you change and recompile it. When it does that it disposes the old classloader. However, the class of the listener is recorded into core structures, as it's added as a listener. At some point, smodel tries to notify your listener but its class is in a disposed classloader. I think that's what happens.

As for the original problem itself. I could think of this solution.

You create a plugin solution for your language, where you implement calculating the index, listening, etc. You define property index, and define a getter for it. In the getter, you attach a listener if needed, via plugin solution facilities, and return a computed value, also via plugin solution. Obviously, the part where you check if listener hasn't been already attached would have to be very fast, as the getter would potentially be queried intensively.

When I say plugin solution, I don't mean that it must be exactly that. It could be just a few classes in their own model that lives in the language. Or even, java classes in, say, behaviour or constraints (where the getter lives) model.

But as I said, you should also detach your listeners. I could think of using ClassLoaderManager.addReloadListener(). It would let you know when modules, and you language module in particular, are reloaded. Detach your listeners then.

0

Great suggestions Daniil! The thing with the classloader is not really intuitive.
The solution with a listener in the getter of the property is quite clever, I will try that.

Thanks a million!

0

Hello

@Daniil

I am trying to use the solution you have explained in your previous post. You wrote that the Classloader is living in the languauge module and that the listener is to be detached when that module is reloaded.

I am not really clear on what you mean with language module. In our project we got several languages and solution ( All together are our "Language"). One is a plugin solutin that starts our SNodeChangeListeners(when the project is loaded) on the models on which we need them.

If this plugin-listener-solution is reloaded we run into problems. I tried detaching and reataching new Listeners to my models when the plugin-listener-solution is reloaded but that didnt work.

Is that the right way to do it and I just have something wrong in my code, or am I complete on the wrong track ?

Greetings Jochen

0

Please sign in to leave a comment.