/* * Copyright 2003-2016 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package jetbrains.mps.text.impl; import jetbrains.mps.extapi.module.TransientSModule; import jetbrains.mps.text.CompatibilityTextUnit; import jetbrains.mps.text.TextGenResult; import jetbrains.mps.text.TextUnit; import jetbrains.mps.textgen.trace.DebugInfo; import jetbrains.mps.textgen.trace.ScopePositionInfo; import jetbrains.mps.textgen.trace.TraceablePositionInfo; import jetbrains.mps.textgen.trace.TracingUtil; import jetbrains.mps.textgen.trace.UnitPositionInfo; import jetbrains.mps.textgen.trace.VarInfo; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.mps.openapi.model.SNode; import org.jetbrains.mps.openapi.model.SNodeReference; import org.jetbrains.mps.openapi.module.SRepository; import java.util.Map; /** * Much like {@link BLDependenciesBuilder}, produce auxiliary artifacts along with text generation process. * * See {@link CompatibilityTextUnit} for ideas how to get rid of [textgen] dependency to [debuginfo-api] */ public class DebugInfoBuilder { private final DebugInfo myDebugInfo = new DebugInfo(); private final SRepository myRepository; /** * @param repository where to look for origin of the traced nodes */ public DebugInfoBuilder(@NotNull SRepository repository) { myRepository = repository; } public DebugInfo build(TextGenResult textResult) { for (TextUnit tu : textResult.getUnits()) { if (tu.getState() == TextUnit.Status.Empty) { continue; } if (!(tu instanceof CompatibilityTextUnit)) { continue; } final CompatibilityTextUnit result = (CompatibilityTextUnit) tu; fillTrace(tu.getFileName(), result.getPositions()); fillScope(tu.getFileName(), result.getScopePositions()); fillUnits(tu.getFileName(), result.getUnitPositions()); } return getDebugInfo(); } /** * @param output node in output/textgen model * @return node in original model, most close ancestor of the one output node originates from */ @Nullable protected SNode getOriginalInputNode(@Nullable SNode output) { while (output != null) { SNode input = output; while (input != null && (input.getModel().getModule() instanceof TransientSModule)) { SNodeReference inputRef = TracingUtil.getInput(input); input = inputRef == null ? null : inputRef.resolve(myRepository); } if (input != null) { return input; } output = output.getParent(); } return null; } /*package*/ void addTraceablePosition(SNode inputNode, String fileName, TraceablePositionInfo positionInfo) { positionInfo.setNodeId(inputNode.getNodeId().toString()); positionInfo.setFileName(fileName); SNode topmostAncestor = inputNode.getContainingRoot(); myDebugInfo.addPosition(positionInfo, topmostAncestor); } /*package*/ void addScopePosition(SNode inputNode, String fileName, ScopePositionInfo positionInfo) { positionInfo.setNodeId(inputNode.getNodeId().toString()); positionInfo.setFileName(fileName); Map<SNode, VarInfo> varMap = positionInfo.getTempVarInfoMap(); for (SNode varNode : varMap.keySet()) { SNode originalVar = getOriginalInputNode(varNode); VarInfo varInfo = varMap.get(varNode); if (originalVar != null) { varInfo.setNodeId(originalVar.getNodeId().toString()); } else { positionInfo.removeVarInfo(varInfo); } } positionInfo.clearTempVarInfoMap(); myDebugInfo.addScopePosition(positionInfo, inputNode.getContainingRoot()); } /*package*/ void addUnitPosition(SNode inputNode, String fileName, UnitPositionInfo positionInfo) { positionInfo.setFileName(fileName); SNode topmostAncestor = null; if (inputNode != null) { positionInfo.setNodeId(inputNode.getNodeId().toString()); topmostAncestor = inputNode.getContainingRoot(); } myDebugInfo.addUnitPosition(positionInfo, topmostAncestor); } /*package*/ DebugInfo getDebugInfo() { return myDebugInfo; } /*package*/ void fillTrace(String fileName, Map<SNode, TraceablePositionInfo> positions) { if (positions == null) { return; } for (SNode out : positions.keySet()) { SNode input = getOriginalInputNode(out); if (input != null) { addTraceablePosition(input, fileName, positions.get(out)); } } } /*package*/ void fillScope(String fileName, Map<SNode, ScopePositionInfo> scopePositions) { if (scopePositions == null) { return; } for (SNode out : scopePositions.keySet()) { SNode input = getOriginalInputNode(out); if (input != null) { addScopePosition(input, fileName, scopePositions.get(out)); } } } /*package*/ void fillUnits(String fileName, Map<SNode, UnitPositionInfo> unitPositions) { if (unitPositions == null) { return; } for (SNode out : unitPositions.keySet()) { SNode input = getOriginalInputNode(out); addUnitPosition(input, fileName, unitPositions.get(out)); } } }