UnsupportedOperationException in PostponedReference.setTargetSModelReference()

Hi all,

After moving into MPS3.2 I got the following exception while rebuilding some of my solutions:
java.lang.UnsupportedOperationException
java.lang.UnsupportedOperationException
 at jetbrains.mps.generator.impl.reference.PostponedReference.setTargetSModelReference(PostponedReference.java:71)
 at jetbrains.mps.smodel.SModel.changeModelReference(SModel.java:954)
 at jetbrains.mps.extapi.model.SModelDescriptorStub.changeModelReference(SModelDescriptorStub.java:339)
 at jetbrains.mps.extapi.model.SModelBase.changeModelReference(SModelBase.java:350)
 at jetbrains.mps.generator.TransientModelsModule.changeModelReference(TransientModelsModule.java:231)
 at jetbrains.mps.generator.impl.GenerationSession.changeModelReference(GenerationSession.java:613)
 at jetbrains.mps.generator.impl.GenerationSession.executeMajorStepInternal(GenerationSession.java:390)
 at jetbrains.mps.generator.impl.GenerationSession.executeMajorStep(GenerationSession.java:321)
 at jetbrains.mps.generator.impl.GenerationSession.generateModel(GenerationSession.java:224)
 at jetbrains.mps.generator.impl.GenerationController.generateModel(GenerationController.java:240)
 at jetbrains.mps.generator.impl.GenerationController.generateModelsInModule(GenerationController.java:153)
 at jetbrains.mps.generator.impl.GenerationController.generate(GenerationController.java:106)
 at jetbrains.mps.generator.GenerationFacade$2$1.compute(GenerationFacade.java:252)
 at jetbrains.mps.generator.GenerationFacade$2$1.compute(GenerationFacade.java:249)
 at jetbrains.mps.ide.undo.WorkbenchUndoHandler.runNonUndoableAction(WorkbenchUndoHandler.java:46)
 at jetbrains.mps.smodel.UndoHelper.runNonUndoableAction(UndoHelper.java:61)
 at jetbrains.mps.generator.GenerationFacade$2.run(GenerationFacade.java:249)
 at jetbrains.mps.ide.smodel.WorkbenchModelAccess$6.compute(WorkbenchModelAccess.java:308)
 at jetbrains.mps.ide.smodel.WorkbenchModelAccess$6.compute(WorkbenchModelAccess.java:303)
 at com.intellij.openapi.application.impl.ApplicationImpl.runReadAction(ApplicationImpl.java:920)
 at jetbrains.mps.ide.smodel.WorkbenchModelAccess.tryRead(WorkbenchModelAccess.java:303)
 at jetbrains.mps.ide.smodel.WorkbenchModelAccess.requireRead(WorkbenchModelAccess.java:340)
 at jetbrains.mps.generator.GenerationFacade.process(GenerationFacade.java:246)
 at jetbrains.mps.generator.GenerationFacade.generateModels(GenerationFacade.java:213)
 at jetbrains.mps.lang.core.plugin.Generate_Facet$Target_generate$1.execute(Generate_Facet.java:470)
 at jetbrains.mps.internal.make.runtime.script.Script$3.invoke(Script.java:292)
 at jetbrains.mps.internal.make.runtime.script.Script$3.invoke(Script.java:235)
 at jetbrains.mps.ide.make.WorkbenchMakeService$Controller.runJobWithMonitor(WorkbenchMakeService.java:327)
 at jetbrains.mps.internal.make.runtime.script.Script.executeTargets(Script.java:235)
 at jetbrains.mps.internal.make.runtime.script.Script.execute(Script.java:213)
 at jetbrains.mps.make.service.CoreMakeTask$1.invoke(CoreMakeTask.java:104)
 at jetbrains.mps.make.service.CoreMakeTask$1.invoke(CoreMakeTask.java:78)
 at jetbrains.mps.make.dependencies.MakeSequence.iterate(MakeSequence.java:59)
 at jetbrains.mps.make.service.CoreMakeTask.doRun(CoreMakeTask.java:78)
 at jetbrains.mps.ide.make.MakeTask$WorkbenchMakeTask.doRun(MakeTask.java:135)
 at jetbrains.mps.make.service.CoreMakeTask.run(CoreMakeTask.java:58)
 at jetbrains.mps.ide.make.MakeTask$2.run(MakeTask.java:64)
 at java.lang.Thread.run(Thread.java:724)

I´ve noticed while reading some of the sources that this will only happen for in place transformations (GenerationSession class implements that logic).

Turning off in place transformation I can rebuild my solutions sucessfully.

Any ideas on this?
2 comments
It is likely that there’s a use of a transformed reference (PostponedReference1) inside a reference macro (PostponedReference2) I.e. if you got roots N1 and N2, with N1 copied, and its references (i.e. this in the nodes under N1) marked as Postponed to get resolved later, and N2 is transformed, and along the transformation, there’s a reference macro, with code that involves references of N1 (i.e. N2.getN1.child.reference which used to point to some other child under N1, aforementioned PostponedReference1). The moment the reference macro is executed, there’s no guarantee that all postponed references in N1 are already replaced with ‘true’ references (the order PostponedReferences get processed is not part of generator contract). One of these postponed reference fails to get replaced and stays in the model, where subsequent change of model reference detects it with the exception.

Of course, this happens with in-place transformation turned on only, as it’s the case when the input model is modified and thus no original reference to follow is available. Possible mitigation is not to use complex queries in reference macros (i.e. those involving traversing different roots; required target might be kept as variable macro, for example).  Yet better alternative is to switch to 3.3, where we don’t replace references in the model any more (postponed references are kept separately, and injected after transformation, when everything is ready, and every reference macro got a chance to traverse original intact input model).

Vaclav (re-posting Artem's answer)
0
Possible mitigation is not to use complex queries in reference macros (i.e. those involving traversing different roots; required target might be kept as variable macro, for example). Yet better alternative is to switch to 3.3, where we don’t replace references in the model any more (postponed references are kept separately, and injected after transformation, when everything is ready, and every reference macro got a chance to traverse original intact input model).


Hi Vaclav,

That is a really good answer! Understood it perfectly, thanks for your help!

Best regards,
Joao
0

Please sign in to leave a comment.