Skip to content

Commit e7db6ae

Browse files
committed
fix(mps-model-adapters): references to non-existing project modules were still resolvable
A project contains a list of references to modules it contains. If the target module exists but isn't registered in the project, an `MPSProjectModuleReference` was still resolved to an `MPSProjectAsNode`.
1 parent 5dec4a8 commit e7db6ae

File tree

3 files changed

+38
-44
lines changed

3 files changed

+38
-44
lines changed

mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSArea.kt

Lines changed: 11 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import jetbrains.mps.smodel.ModelImports
77
import org.jetbrains.mps.openapi.model.SNodeReference
88
import org.jetbrains.mps.openapi.module.SRepository
99
import org.jetbrains.mps.openapi.persistence.PersistenceFacade
10+
import org.modelix.model.api.BuiltinLanguages
1011
import org.modelix.model.api.ChildLinkReferenceByUID
1112
import org.modelix.model.api.IBranch
1213
import org.modelix.model.api.IConcept
@@ -264,48 +265,19 @@ data class MPSArea(val repository: SRepository) : IArea, IAreaReference {
264265
}
265266

266267
private fun resolveMPSProjectReference(ref: INodeReference): MPSProjectAsNode? {
267-
val projectName = (MPSProjectReference.tryConvert(ref) ?: return null).projectName
268-
269-
val project = MPSProjectAsNode.getContextProjects().find { it.getName() == projectName }
270-
271-
return project?.let { MPSProjectAsNode(it) }
268+
return MPSRepositoryAsNode(repository)
269+
.getChildren(BuiltinLanguages.MPSRepositoryConcepts.Repository.projects.toReference())
270+
.find { it.getNodeReference() == ref }
271+
?.let { it as MPSProjectAsNode }
272272
}
273273

274274
private fun resolveMPSProjectModuleReference(ref: INodeReference): MPSProjectModuleAsNode? {
275-
val serialized = ref.serialize()
276-
val moduleRef = if (ref is MPSProjectModuleReference) {
277-
ref.moduleRef
278-
} else {
279-
val serializedModuleRef = serialized
280-
.substringAfter("${MPSProjectModuleReference.PREFIX}:")
281-
.substringBefore(MPSProjectModuleReference.SEPARATOR)
282-
MPSReferenceParser.parseSModuleReference(serializedModuleRef)
283-
}
284-
285-
val projectRef = if (ref is MPSProjectModuleReference) {
286-
ref.projectRef
287-
} else {
288-
// XXX Prefix of `projectRef` is not checked.
289-
// `projectRef` might actually be a ref to anything.
290-
// This might trigger unexpected resolution results and undefined behavior.
291-
// Similar missing checks exist for other references in `MPSArea`.
292-
// See https://issues.modelix.org/issue/MODELIX-923
293-
val projectRef = serialized.substringAfter(MPSProjectModuleReference.SEPARATOR)
294-
NodeReference(projectRef)
295-
}
296-
297-
val resolvedNodeForProject = resolveNode(projectRef)?.asWritableNode() ?: return null
298-
check(resolvedNodeForProject is MPSProjectAsNode) {
299-
"Resolved node `$resolvedNodeForProject` does not represent a project."
300-
}
301-
val resolvedProject = resolvedNodeForProject.project
302-
303-
return moduleRef.resolve(repository)?.let {
304-
MPSProjectModuleAsNode(
305-
project = resolvedProject,
306-
module = it,
307-
)
308-
}
275+
val ref = MPSProjectModuleReference.tryConvert(ref) ?: return null
276+
val project = resolveNode(ref.projectRef)?.asWritableNode() ?: return null
277+
return project
278+
.getChildren(BuiltinLanguages.MPSRepositoryConcepts.Project.projectModules.toReference())
279+
.find { it.getNodeReference() == ref }
280+
?.let { it as MPSProjectModuleAsNode }
309281
}
310282

311283
private fun resolveMPSSingleLanguageDependencyReference(ref: INodeReference): MPSSingleLanguageDependencyAsNode? {

mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSProjectAsNode.kt

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import org.modelix.model.api.IPropertyReference
1111
import org.modelix.model.api.IReferenceLinkReference
1212
import org.modelix.model.api.IWritableNode
1313
import org.modelix.mps.api.ModelixMpsApi
14+
import org.modelix.mps.multiplatform.model.MPSModuleReference
1415

1516
data class MPSProjectAsNode(val project: IMPSProject) : MPSGenericNodeAdapter<IMPSProject>() {
1617

@@ -74,13 +75,18 @@ data class MPSProjectAsNode(val project: IMPSProject) : MPSGenericNodeAdapter<IM
7475
}
7576

7677
override fun addNew(element: IMPSProject, index: Int, sourceNode: SpecWithResolvedConcept): IWritableNode {
77-
val targetModule = requireNotNull(sourceNode.getNode().getReferenceTarget(BuiltinLanguages.MPSRepositoryConcepts.ModuleReference.module.toReference())) {
78+
val targetModuleRef = requireNotNull(sourceNode.getNode().getReferenceTargetRef(BuiltinLanguages.MPSRepositoryConcepts.ModuleReference.module.toReference())) {
7879
"Reference to module isn't set"
7980
}
80-
val targetName = targetModule.getPropertyValue(BuiltinLanguages.jetbrains_mps_lang_core.INamedConcept.name.toReference())
81-
val targetId = requireNotNull(targetModule.getPropertyValue(BuiltinLanguages.MPSRepositoryConcepts.Module.id.toReference())) {
82-
"Module ID isn't set: $targetModule"
83-
}.let { ModuleId.fromString(it) }
81+
val mpsTargetModuleRef = requireNotNull(MPSModuleReference.tryConvert(targetModuleRef)) {
82+
"Not an MPS module reference: $targetModuleRef"
83+
}
84+
val targetModule = sourceNode.getNode().getReferenceTarget(BuiltinLanguages.MPSRepositoryConcepts.ModuleReference.module.toReference())
85+
val targetName = targetModule?.getPropertyValue(BuiltinLanguages.jetbrains_mps_lang_core.INamedConcept.name.toReference())
86+
val targetId = (
87+
targetModule?.getPropertyValue(BuiltinLanguages.MPSRepositoryConcepts.Module.id.toReference())
88+
?: mpsTargetModuleRef.moduleId
89+
).let { ModuleId.fromString(it) }
8490
val ref = PersistenceFacade.getInstance().createModuleReference(targetId, targetName)
8591
val resolvedModule = checkNotNull(ref.resolve(element.getRepository())) { "Module not found: $ref" }
8692
element.addModule(resolvedModule)

mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSReferences.kt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import org.jetbrains.mps.openapi.module.SModuleId
99
import org.jetbrains.mps.openapi.module.SModuleReference
1010
import org.jetbrains.mps.openapi.persistence.PersistenceFacade
1111
import org.modelix.model.api.INodeReference
12+
import org.modelix.model.api.NodeReference
1213
import org.modelix.mps.multiplatform.model.MPSModelReference
1314
import org.modelix.mps.multiplatform.model.MPSModuleReference
1415
import org.modelix.mps.multiplatform.model.MPSNodeReference
@@ -142,7 +143,22 @@ data class MPSProjectModuleReference(val moduleRef: SModuleReference, val projec
142143

143144
companion object {
144145
internal const val PREFIX = "mps-project-module"
146+
internal const val PREFIX_COLON = "$PREFIX:"
145147
internal const val SEPARATOR = "#IN#"
148+
149+
fun tryConvert(ref: INodeReference): MPSProjectModuleReference? {
150+
if (ref is MPSProjectModuleReference) return ref
151+
val serialized = ref.serialize()
152+
if (!serialized.startsWith(PREFIX_COLON)) return null
153+
val moduleRef = serialized
154+
.substringAfter(PREFIX_COLON)
155+
.substringBefore(SEPARATOR)
156+
.let { MPSReferenceParser.parseSModuleReference(it) }
157+
val projectRef = NodeReference(serialized.substringAfter(SEPARATOR))
158+
.let { MPSProjectReference.tryConvert(it) }
159+
.let { requireNotNull(it) { "Invalid project reference: $it" } }
160+
return MPSProjectModuleReference(moduleRef, projectRef)
161+
}
146162
}
147163

148164
override fun serialize(): String {

0 commit comments

Comments
 (0)