MLIR  22.0.0git
DebugImporter.cpp
Go to the documentation of this file.
1 //===- DebugImporter.cpp - LLVM to MLIR Debug conversion ------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "DebugImporter.h"
11 #include "mlir/IR/Attributes.h"
13 #include "mlir/IR/Location.h"
14 #include "llvm/ADT/STLExtras.h"
15 #include "llvm/ADT/TypeSwitch.h"
16 #include "llvm/BinaryFormat/Dwarf.h"
17 #include "llvm/IR/Constants.h"
18 #include "llvm/IR/DebugInfoMetadata.h"
19 #include "llvm/IR/Metadata.h"
20 
21 using namespace mlir;
22 using namespace mlir::LLVM;
23 using namespace mlir::LLVM::detail;
24 
25 DebugImporter::DebugImporter(ModuleOp mlirModule,
26  bool dropDICompositeTypeElements)
27  : cache([&](llvm::DINode *node) { return createRecSelf(node); }),
28  context(mlirModule.getContext()), mlirModule(mlirModule),
29  dropDICompositeTypeElements(dropDICompositeTypeElements) {}
30 
32  llvm::DISubprogram *subprogram = func->getSubprogram();
33  if (!subprogram)
34  return UnknownLoc::get(context);
35 
36  // Add a fused location to link the subprogram information.
37  StringAttr funcName = StringAttr::get(context, subprogram->getName());
38  StringAttr fileName = StringAttr::get(context, subprogram->getFilename());
40  {NameLoc::get(funcName),
41  FileLineColLoc::get(fileName, subprogram->getLine(), /*column=*/0)},
42  translate(subprogram), context);
43 }
44 
45 //===----------------------------------------------------------------------===//
46 // Attributes
47 //===----------------------------------------------------------------------===//
48 
49 DIBasicTypeAttr DebugImporter::translateImpl(llvm::DIBasicType *node) {
50  return DIBasicTypeAttr::get(context, node->getTag(), node->getName(),
51  node->getSizeInBits(), node->getEncoding());
52 }
53 
54 DICompileUnitAttr DebugImporter::translateImpl(llvm::DICompileUnit *node) {
55  std::optional<DIEmissionKind> emissionKind =
56  symbolizeDIEmissionKind(node->getEmissionKind());
57  std::optional<DINameTableKind> nameTableKind = symbolizeDINameTableKind(
58  static_cast<
59  std::underlying_type_t<llvm::DICompileUnit::DebugNameTableKind>>(
60  node->getNameTableKind()));
62  context, getOrCreateDistinctID(node), node->getSourceLanguage(),
63  translate(node->getFile()), getStringAttrOrNull(node->getRawProducer()),
64  node->isOptimized(), emissionKind.value(), nameTableKind.value());
65 }
66 
67 DICompositeTypeAttr DebugImporter::translateImpl(llvm::DICompositeType *node) {
68  std::optional<DIFlags> flags = symbolizeDIFlags(node->getFlags());
69  SmallVector<DINodeAttr> elements;
70 
71  // A vector always requires an element.
72  bool isVectorType = flags && bitEnumContainsAll(*flags, DIFlags::Vector);
73  if (isVectorType || !dropDICompositeTypeElements) {
74  for (llvm::DINode *element : node->getElements()) {
75  assert(element && "expected a non-null element type");
76  elements.push_back(translate(element));
77  }
78  }
79  // Drop the elements parameter if any of the elements are invalid.
80  if (llvm::is_contained(elements, nullptr))
81  elements.clear();
82  DITypeAttr baseType = translate(node->getBaseType());
83  // Arrays require a base type, otherwise the debug metadata is considered to
84  // be malformed.
85  if (node->getTag() == llvm::dwarf::DW_TAG_array_type && !baseType)
86  return nullptr;
88  context, node->getTag(), getStringAttrOrNull(node->getRawName()),
89  translate(node->getFile()), node->getLine(), translate(node->getScope()),
90  baseType, flags.value_or(DIFlags::Zero), node->getSizeInBits(),
91  node->getAlignInBits(), elements,
92  translateExpression(node->getDataLocationExp()),
93  translateExpression(node->getRankExp()),
94  translateExpression(node->getAllocatedExp()),
95  translateExpression(node->getAssociatedExp()));
96 }
97 
98 DIDerivedTypeAttr DebugImporter::translateImpl(llvm::DIDerivedType *node) {
99  // Return nullptr if the base type is invalid.
100  DITypeAttr baseType = translate(node->getBaseType());
101  if (node->getBaseType() && !baseType)
102  return nullptr;
103  DINodeAttr extraData =
104  translate(dyn_cast_or_null<llvm::DINode>(node->getExtraData()));
105  return DIDerivedTypeAttr::get(
106  context, node->getTag(), getStringAttrOrNull(node->getRawName()),
107  baseType, node->getSizeInBits(), node->getAlignInBits(),
108  node->getOffsetInBits(), node->getDWARFAddressSpace(), extraData);
109 }
110 
111 DIStringTypeAttr DebugImporter::translateImpl(llvm::DIStringType *node) {
112  return DIStringTypeAttr::get(
113  context, node->getTag(), getStringAttrOrNull(node->getRawName()),
114  node->getSizeInBits(), node->getAlignInBits(),
115  translate(node->getStringLength()),
116  translateExpression(node->getStringLengthExp()),
117  translateExpression(node->getStringLocationExp()), node->getEncoding());
118 }
119 
120 DIFileAttr DebugImporter::translateImpl(llvm::DIFile *node) {
121  return DIFileAttr::get(context, node->getFilename(), node->getDirectory());
122 }
123 
124 DILabelAttr DebugImporter::translateImpl(llvm::DILabel *node) {
125  // Return nullptr if the scope or type is a cyclic dependency.
126  DIScopeAttr scope = translate(node->getScope());
127  if (node->getScope() && !scope)
128  return nullptr;
129  return DILabelAttr::get(context, scope,
130  getStringAttrOrNull(node->getRawName()),
131  translate(node->getFile()), node->getLine());
132 }
133 
134 DILexicalBlockAttr DebugImporter::translateImpl(llvm::DILexicalBlock *node) {
135  // Return nullptr if the scope or type is a cyclic dependency.
136  DIScopeAttr scope = translate(node->getScope());
137  if (node->getScope() && !scope)
138  return nullptr;
139  return DILexicalBlockAttr::get(context, scope, translate(node->getFile()),
140  node->getLine(), node->getColumn());
141 }
142 
143 DILexicalBlockFileAttr
144 DebugImporter::translateImpl(llvm::DILexicalBlockFile *node) {
145  // Return nullptr if the scope or type is a cyclic dependency.
146  DIScopeAttr scope = translate(node->getScope());
147  if (node->getScope() && !scope)
148  return nullptr;
149  return DILexicalBlockFileAttr::get(context, scope, translate(node->getFile()),
150  node->getDiscriminator());
151 }
152 
153 DIGlobalVariableAttr
154 DebugImporter::translateImpl(llvm::DIGlobalVariable *node) {
155  // Names of DIGlobalVariables can be empty. MLIR models them as null, instead
156  // of empty strings, so this special handling is necessary.
157  auto convertToStringAttr = [&](StringRef name) -> StringAttr {
158  if (name.empty())
159  return {};
160  return StringAttr::get(context, node->getName());
161  };
163  context, translate(node->getScope()),
164  convertToStringAttr(node->getName()),
165  convertToStringAttr(node->getLinkageName()), translate(node->getFile()),
166  node->getLine(), translate(node->getType()), node->isLocalToUnit(),
167  node->isDefinition(), node->getAlignInBits());
168 }
169 
170 DILocalVariableAttr DebugImporter::translateImpl(llvm::DILocalVariable *node) {
171  // Return nullptr if the scope or type is a cyclic dependency.
172  DIScopeAttr scope = translate(node->getScope());
173  if (node->getScope() && !scope)
174  return nullptr;
176  context, scope, getStringAttrOrNull(node->getRawName()),
177  translate(node->getFile()), node->getLine(), node->getArg(),
178  node->getAlignInBits(), translate(node->getType()),
179  symbolizeDIFlags(node->getFlags()).value_or(DIFlags::Zero));
180 }
181 
182 DIVariableAttr DebugImporter::translateImpl(llvm::DIVariable *node) {
183  return cast<DIVariableAttr>(translate(static_cast<llvm::DINode *>(node)));
184 }
185 
186 DIScopeAttr DebugImporter::translateImpl(llvm::DIScope *node) {
187  return cast<DIScopeAttr>(translate(static_cast<llvm::DINode *>(node)));
188 }
189 
190 DIModuleAttr DebugImporter::translateImpl(llvm::DIModule *node) {
191  return DIModuleAttr::get(
192  context, translate(node->getFile()), translate(node->getScope()),
193  getStringAttrOrNull(node->getRawName()),
194  getStringAttrOrNull(node->getRawConfigurationMacros()),
195  getStringAttrOrNull(node->getRawIncludePath()),
196  getStringAttrOrNull(node->getRawAPINotesFile()), node->getLineNo(),
197  node->getIsDecl());
198 }
199 
200 DINamespaceAttr DebugImporter::translateImpl(llvm::DINamespace *node) {
201  return DINamespaceAttr::get(context, getStringAttrOrNull(node->getRawName()),
202  translate(node->getScope()),
203  node->getExportSymbols());
204 }
205 
206 DIImportedEntityAttr
207 DebugImporter::translateImpl(llvm::DIImportedEntity *node) {
208  SmallVector<DINodeAttr> elements;
209  for (llvm::DINode *element : node->getElements()) {
210  assert(element && "expected a non-null element type");
211  elements.push_back(translate(element));
212  }
213 
215  context, node->getTag(), translate(node->getScope()),
216  translate(node->getEntity()), translate(node->getFile()), node->getLine(),
217  getStringAttrOrNull(node->getRawName()), elements);
218 }
219 
220 DISubprogramAttr DebugImporter::translateImpl(llvm::DISubprogram *node) {
221  // Only definitions require a distinct identifier.
223  if (node->isDistinct())
224  id = getOrCreateDistinctID(node);
225 
226  // Return nullptr if the scope or type is invalid.
227  DIScopeAttr scope = translate(node->getScope());
228  if (node->getScope() && !scope)
229  return nullptr;
230  std::optional<DISubprogramFlags> subprogramFlags =
231  symbolizeDISubprogramFlags(node->getSubprogram()->getSPFlags());
232  assert(subprogramFlags && "expected valid subprogram flags");
233  DISubroutineTypeAttr type = translate(node->getType());
234  if (node->getType() && !type)
235  return nullptr;
236 
237  // Convert the retained nodes but drop all of them if one of them is invalid.
238  SmallVector<DINodeAttr> retainedNodes;
239  for (llvm::DINode *retainedNode : node->getRetainedNodes())
240  retainedNodes.push_back(translate(retainedNode));
241  if (llvm::is_contained(retainedNodes, nullptr))
242  retainedNodes.clear();
243 
244  SmallVector<DINodeAttr> annotations;
245  // We currently only support `string` values for annotations on the MLIR side.
246  // Theoretically we could support other primitives, but LLVM is not using
247  // other types in practice.
248  if (llvm::DINodeArray rawAnns = node->getAnnotations(); rawAnns) {
249  for (size_t i = 0, e = rawAnns->getNumOperands(); i < e; ++i) {
250  const llvm::MDTuple *tuple = cast<llvm::MDTuple>(rawAnns->getOperand(i));
251  if (tuple->getNumOperands() != 2)
252  continue;
253  const llvm::MDString *name = cast<llvm::MDString>(tuple->getOperand(0));
254  const llvm::MDString *value =
255  dyn_cast<llvm::MDString>(tuple->getOperand(1));
256  if (name && value) {
257  annotations.push_back(DIAnnotationAttr::get(
258  context, StringAttr::get(context, name->getString()),
259  StringAttr::get(context, value->getString())));
260  }
261  }
262  }
263 
264  return DISubprogramAttr::get(context, id, translate(node->getUnit()), scope,
265  getStringAttrOrNull(node->getRawName()),
266  getStringAttrOrNull(node->getRawLinkageName()),
267  translate(node->getFile()), node->getLine(),
268  node->getScopeLine(), *subprogramFlags, type,
269  retainedNodes, annotations);
270 }
271 
272 DISubrangeAttr DebugImporter::translateImpl(llvm::DISubrange *node) {
273  auto getAttrOrNull = [&](llvm::DISubrange::BoundType data) -> Attribute {
274  if (data.isNull())
275  return nullptr;
276  if (auto *constInt = dyn_cast<llvm::ConstantInt *>(data))
277  return IntegerAttr::get(IntegerType::get(context, 64),
278  constInt->getSExtValue());
279  if (auto *expr = dyn_cast<llvm::DIExpression *>(data))
280  return translateExpression(expr);
281  if (auto *var = dyn_cast<llvm::DIVariable *>(data)) {
282  if (auto *local = dyn_cast<llvm::DILocalVariable>(var))
283  return translate(local);
284  if (auto *global = dyn_cast<llvm::DIGlobalVariable>(var))
285  return translate(global);
286  return nullptr;
287  }
288  return nullptr;
289  };
290  Attribute count = getAttrOrNull(node->getCount());
291  Attribute upperBound = getAttrOrNull(node->getUpperBound());
292  // Either count or the upper bound needs to be present. Otherwise, the
293  // metadata is invalid. The conversion might fail due to unsupported DI nodes.
294  if (!count && !upperBound)
295  return {};
296  return DISubrangeAttr::get(context, count,
297  getAttrOrNull(node->getLowerBound()), upperBound,
298  getAttrOrNull(node->getStride()));
299 }
300 
301 DICommonBlockAttr DebugImporter::translateImpl(llvm::DICommonBlock *node) {
302  return DICommonBlockAttr::get(context, translate(node->getScope()),
303  translate(node->getDecl()),
304  getStringAttrOrNull(node->getRawName()),
305  translate(node->getFile()), node->getLineNo());
306 }
307 
308 DIGenericSubrangeAttr
309 DebugImporter::translateImpl(llvm::DIGenericSubrange *node) {
310  auto getAttrOrNull =
312  if (data.isNull())
313  return nullptr;
314  if (auto *expr = dyn_cast<llvm::DIExpression *>(data))
315  return translateExpression(expr);
316  if (auto *var = dyn_cast<llvm::DIVariable *>(data)) {
317  if (auto *local = dyn_cast<llvm::DILocalVariable>(var))
318  return translate(local);
319  if (auto *global = dyn_cast<llvm::DIGlobalVariable>(var))
320  return translate(global);
321  return nullptr;
322  }
323  return nullptr;
324  };
325  Attribute count = getAttrOrNull(node->getCount());
326  Attribute upperBound = getAttrOrNull(node->getUpperBound());
327  Attribute lowerBound = getAttrOrNull(node->getLowerBound());
328  Attribute stride = getAttrOrNull(node->getStride());
329  // Either count or the upper bound needs to be present. Otherwise, the
330  // metadata is invalid.
331  if (!count && !upperBound)
332  return {};
333  return DIGenericSubrangeAttr::get(context, count, lowerBound, upperBound,
334  stride);
335 }
336 
337 DISubroutineTypeAttr
338 DebugImporter::translateImpl(llvm::DISubroutineType *node) {
340  for (llvm::DIType *type : node->getTypeArray()) {
341  if (!type) {
342  // A nullptr entry may appear at the beginning or the end of the
343  // subroutine types list modeling either a void result type or the type of
344  // a variadic argument. Translate the nullptr to an explicit
345  // DINullTypeAttr since the attribute list cannot contain a nullptr entry.
346  types.push_back(DINullTypeAttr::get(context));
347  continue;
348  }
349  types.push_back(translate(type));
350  }
351  // Return nullptr if any of the types is invalid.
352  if (llvm::is_contained(types, nullptr))
353  return nullptr;
354  return DISubroutineTypeAttr::get(context, node->getCC(), types);
355 }
356 
357 DITypeAttr DebugImporter::translateImpl(llvm::DIType *node) {
358  return cast<DITypeAttr>(translate(static_cast<llvm::DINode *>(node)));
359 }
360 
362  if (!node)
363  return nullptr;
364 
365  // Check for a cached instance.
366  auto cacheEntry = cache.lookupOrInit(node);
367  if (std::optional<DINodeAttr> result = cacheEntry.get())
368  return *result;
369 
370  // Convert the debug metadata if possible.
371  auto translateNode = [this](llvm::DINode *node) -> DINodeAttr {
372  if (auto *casted = dyn_cast<llvm::DIBasicType>(node))
373  return translateImpl(casted);
374  if (auto *casted = dyn_cast<llvm::DICommonBlock>(node))
375  return translateImpl(casted);
376  if (auto *casted = dyn_cast<llvm::DICompileUnit>(node))
377  return translateImpl(casted);
378  if (auto *casted = dyn_cast<llvm::DICompositeType>(node))
379  return translateImpl(casted);
380  if (auto *casted = dyn_cast<llvm::DIDerivedType>(node))
381  return translateImpl(casted);
382  if (auto *casted = dyn_cast<llvm::DIStringType>(node))
383  return translateImpl(casted);
384  if (auto *casted = dyn_cast<llvm::DIFile>(node))
385  return translateImpl(casted);
386  if (auto *casted = dyn_cast<llvm::DIGlobalVariable>(node))
387  return translateImpl(casted);
388  if (auto *casted = dyn_cast<llvm::DIImportedEntity>(node))
389  return translateImpl(casted);
390  if (auto *casted = dyn_cast<llvm::DILabel>(node))
391  return translateImpl(casted);
392  if (auto *casted = dyn_cast<llvm::DILexicalBlock>(node))
393  return translateImpl(casted);
394  if (auto *casted = dyn_cast<llvm::DILexicalBlockFile>(node))
395  return translateImpl(casted);
396  if (auto *casted = dyn_cast<llvm::DILocalVariable>(node))
397  return translateImpl(casted);
398  if (auto *casted = dyn_cast<llvm::DIModule>(node))
399  return translateImpl(casted);
400  if (auto *casted = dyn_cast<llvm::DINamespace>(node))
401  return translateImpl(casted);
402  if (auto *casted = dyn_cast<llvm::DISubprogram>(node))
403  return translateImpl(casted);
404  if (auto *casted = dyn_cast<llvm::DISubrange>(node))
405  return translateImpl(casted);
406  if (auto *casted = dyn_cast<llvm::DIGenericSubrange>(node))
407  return translateImpl(casted);
408  if (auto *casted = dyn_cast<llvm::DISubroutineType>(node))
409  return translateImpl(casted);
410  return nullptr;
411  };
412  if (DINodeAttr attr = translateNode(node)) {
413  // If this node was repeated, lookup its recursive ID and assign it to the
414  // base result.
415  if (cacheEntry.wasRepeated()) {
416  DistinctAttr recId = nodeToRecId.lookup(node);
417  auto recType = cast<DIRecursiveTypeAttrInterface>(attr);
418  attr = cast<DINodeAttr>(recType.withRecId(recId));
419  }
420  cacheEntry.resolve(attr);
421  return attr;
422  }
423  cacheEntry.resolve(nullptr);
424  return nullptr;
425 }
426 
427 /// Get the `getRecSelf` constructor for the translated type of `node` if its
428 /// translated DITypeAttr supports recursion. Otherwise, returns nullptr.
429 static function_ref<DIRecursiveTypeAttrInterface(DistinctAttr)>
430 getRecSelfConstructor(llvm::DINode *node) {
431  using CtorType = function_ref<DIRecursiveTypeAttrInterface(DistinctAttr)>;
433  .Case([&](llvm::DICompositeType *) {
434  return CtorType(DICompositeTypeAttr::getRecSelf);
435  })
436  .Case([&](llvm::DISubprogram *) {
437  return CtorType(DISubprogramAttr::getRecSelf);
438  })
439  .Default(CtorType());
440 }
441 
442 std::optional<DINodeAttr> DebugImporter::createRecSelf(llvm::DINode *node) {
443  auto recSelfCtor = getRecSelfConstructor(node);
444  if (!recSelfCtor)
445  return std::nullopt;
446 
447  // The original node may have already been assigned a recursive ID from
448  // a different self-reference. Use that if possible.
449  DistinctAttr recId = nodeToRecId.lookup(node);
450  if (!recId) {
451  recId = DistinctAttr::create(UnitAttr::get(context));
452  nodeToRecId[node] = recId;
453  }
454  DIRecursiveTypeAttrInterface recSelf = recSelfCtor(recId);
455  return cast<DINodeAttr>(recSelf);
456 }
457 
458 //===----------------------------------------------------------------------===//
459 // Locations
460 //===----------------------------------------------------------------------===//
461 
462 Location DebugImporter::translateLoc(llvm::DILocation *loc) {
463  if (!loc)
464  return UnknownLoc::get(context);
465 
466  // Get the file location of the instruction.
467  Location result = FileLineColLoc::get(context, loc->getFilename(),
468  loc->getLine(), loc->getColumn());
469 
470  // Add scope information.
471  assert(loc->getScope() && "expected non-null scope");
472  result = FusedLocWith<DIScopeAttr>::get({result}, translate(loc->getScope()),
473  context);
474 
475  // Add call site information, if available.
476  if (llvm::DILocation *inlinedAt = loc->getInlinedAt())
477  result = CallSiteLoc::get(result, translateLoc(inlinedAt));
478 
479  return result;
480 }
481 
482 DIExpressionAttr DebugImporter::translateExpression(llvm::DIExpression *node) {
483  if (!node)
484  return nullptr;
485 
487 
488  // Begin processing the operations.
489  for (const llvm::DIExpression::ExprOperand &op : node->expr_ops()) {
490  SmallVector<uint64_t> operands;
491  operands.reserve(op.getNumArgs());
492  for (const auto &i : llvm::seq(op.getNumArgs()))
493  operands.push_back(op.getArg(i));
494  const auto attr = DIExpressionElemAttr::get(context, op.getOp(), operands);
495  ops.push_back(attr);
496  }
497  return DIExpressionAttr::get(context, ops);
498 }
499 
501  llvm::DIGlobalVariableExpression *node) {
503  context, translate(node->getVariable()),
504  translateExpression(node->getExpression()));
505 }
506 
507 StringAttr DebugImporter::getStringAttrOrNull(llvm::MDString *stringNode) {
508  if (!stringNode)
509  return StringAttr();
510  return StringAttr::get(context, stringNode->getString());
511 }
512 
513 DistinctAttr DebugImporter::getOrCreateDistinctID(llvm::DINode *node) {
514  DistinctAttr &id = nodeToDistinctAttr[node];
515  if (!id)
516  id = DistinctAttr::create(UnitAttr::get(context));
517  return id;
518 }
static function_ref< DIRecursiveTypeAttrInterface(DistinctAttr)> getRecSelfConstructor(llvm::DINode *node)
Get the getRecSelf constructor for the translated type of node if its translated DITypeAttr supports ...
Attributes are known-constant values of operations.
Definition: Attributes.h:25
An attribute that associates a referenced attribute with a unique identifier.
static DistinctAttr create(Attribute referencedAttr)
Creates a distinct attribute that associates a referenced attribute with a unique identifier.
static FileLineColLoc get(StringAttr filename, unsigned line, unsigned column)
Definition: Location.cpp:157
This class represents the base attribute for all debug info attributes.
Definition: LLVMAttrs.h:28
This class represents a LLVM attribute that describes a debug info scope.
Definition: LLVMAttrs.h:37
This class represents a LLVM attribute that describes a debug info type.
Definition: LLVMAttrs.h:56
This class represents a LLVM attribute that describes a debug info variable.
Definition: LLVMAttrs.h:65
DINodeAttr translate(llvm::DINode *node)
Translates the given LLVM debug metadata to MLIR.
DIExpressionAttr translateExpression(llvm::DIExpression *node)
Translates the LLVM DWARF expression metadata to MLIR.
DIGlobalVariableExpressionAttr translateGlobalVariableExpression(llvm::DIGlobalVariableExpression *node)
Translates the LLVM DWARF global variable expression metadata to MLIR.
Location translateLoc(llvm::DILocation *loc)
Translates the given LLVM debug location to an MLIR location.
Location translateFuncLocation(llvm::Function *func)
Translates the debug information for the given function into a Location.
DebugImporter(ModuleOp mlirModule, bool dropDICompositeTypeElements)
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition: Location.h:76
The OpAsmOpInterface, see OpAsmInterface.td for more details.
Definition: CallGraph.h:229
BoundType
The type of bound: equal, lower bound or upper bound.
Include the generated interface declarations.
auto get(MLIRContext *context, Ts &&...params)
Helper method that injects context only if needed, this helps unify some of the attribute constructio...