llvm::SmallVector<MlirType, 4> argTypes;
argTypes.reserve(pyArgTypes.size());
for (auto &pyArg : pyArgTypes) {
- argTypes.push_back(pyArg.cast<PyType &>().type);
+ argTypes.push_back(pyArg.cast<PyType &>());
}
MlirBlock block = mlirBlockCreate(argTypes.size(), argTypes.data());
// PyLocation
//------------------------------------------------------------------------------
+py::object PyLocation::getCapsule() {
+ return py::reinterpret_steal<py::object>(mlirPythonLocationToCapsule(*this));
+}
+
+PyLocation PyLocation::createFromCapsule(py::object capsule) {
+ MlirLocation rawLoc = mlirPythonCapsuleToLocation(capsule.ptr());
+ if (mlirLocationIsNull(rawLoc))
+ throw py::error_already_set();
+ return PyLocation(PyMlirContext::forContext(mlirLocationGetContext(rawLoc)),
+ rawLoc);
+}
+
py::object PyLocation::contextEnter() {
return PyThreadContextEntry::pushLocation(*this);
}
// TODO: Verify result type originate from the same context.
if (!result)
throw SetPyError(PyExc_ValueError, "result type cannot be None");
- mlirResults.push_back(result->type);
+ mlirResults.push_back(*result);
}
}
// Unpack/validate attributes.
auto name = it.first.cast<std::string>();
auto &attribute = it.second.cast<PyAttribute &>();
// TODO: Verify attribute originates from the same context.
- mlirAttributes.emplace_back(std::move(name), attribute.attr);
+ mlirAttributes.emplace_back(std::move(name), attribute);
}
}
// Unpack/validate successors.
// Apply unpacked/validated to the operation state. Beyond this
// point, exceptions cannot be thrown or else the state will leak.
MlirOperationState state =
- mlirOperationStateGet(toMlirStringRef(name), location->loc);
+ mlirOperationStateGet(toMlirStringRef(name), location);
if (!mlirOperands.empty())
mlirOperationStateAddOperands(&state, mlirOperands.size(),
mlirOperands.data());
return mlirAttributeEqual(attr, other.attr);
}
+py::object PyAttribute::getCapsule() {
+ return py::reinterpret_steal<py::object>(mlirPythonAttributeToCapsule(*this));
+}
+
+PyAttribute PyAttribute::createFromCapsule(py::object capsule) {
+ MlirAttribute rawAttr = mlirPythonCapsuleToAttribute(capsule.ptr());
+ if (mlirAttributeIsNull(rawAttr))
+ throw py::error_already_set();
+ return PyAttribute(
+ PyMlirContext::forContext(mlirAttributeGetContext(rawAttr)), rawAttr);
+}
+
//------------------------------------------------------------------------------
// PyNamedAttribute.
//------------------------------------------------------------------------------
return mlirTypeEqual(type, other.type);
}
+py::object PyType::getCapsule() {
+ return py::reinterpret_steal<py::object>(mlirPythonTypeToCapsule(*this));
+}
+
+PyType PyType::createFromCapsule(py::object capsule) {
+ MlirType rawType = mlirPythonCapsuleToType(capsule.ptr());
+ if (mlirTypeIsNull(rawType))
+ throw py::error_already_set();
+ return PyType(PyMlirContext::forContext(mlirTypeGetContext(rawType)),
+ rawType);
+}
+
//------------------------------------------------------------------------------
// PyValue and subclases.
//------------------------------------------------------------------------------
void dunderSetItem(const std::string &name, PyAttribute attr) {
mlirOperationSetAttributeByName(operation->get(), toMlirStringRef(name),
- attr.attr);
+ attr);
}
void dunderDelItem(const std::string &name) {
: PyConcreteAttribute(orig.getContext(), castFrom(orig)) {}
static MlirAttribute castFrom(PyAttribute &orig) {
- if (!DerivedTy::isaFunction(orig.attr)) {
+ if (!DerivedTy::isaFunction(orig)) {
auto origRepr = py::repr(py::cast(orig)).cast<std::string>();
throw SetPyError(PyExc_ValueError,
llvm::Twine("Cannot cast attribute to ") +
DerivedTy::pyClassName + " (from " + origRepr + ")");
}
- return orig.attr;
+ return orig;
}
static void bind(py::module &m) {
c.def_static(
"get",
[](PyType &type, double value, DefaultingPyLocation loc) {
- MlirAttribute attr =
- mlirFloatAttrDoubleGetChecked(type.type, value, loc->loc);
+ MlirAttribute attr = mlirFloatAttrDoubleGetChecked(type, value, loc);
// TODO: Rework error reporting once diagnostic engine is exposed
// in C API.
if (mlirAttributeIsNull(attr)) {
c.def_property_readonly(
"value",
[](PyFloatAttribute &self) {
- return mlirFloatAttrGetValueDouble(self.attr);
+ return mlirFloatAttrGetValueDouble(self);
},
"Returns the value of the float point attribute");
}
c.def_static(
"get",
[](PyType &type, int64_t value) {
- MlirAttribute attr = mlirIntegerAttrGet(type.type, value);
+ MlirAttribute attr = mlirIntegerAttrGet(type, value);
return PyIntegerAttribute(type.getContext(), attr);
},
py::arg("type"), py::arg("value"),
c.def_property_readonly(
"value",
[](PyIntegerAttribute &self) {
- return mlirIntegerAttrGetValueInt(self.attr);
+ return mlirIntegerAttrGetValueInt(self);
},
"Returns the value of the integer attribute");
}
"Gets an uniqued bool attribute");
c.def_property_readonly(
"value",
- [](PyBoolAttribute &self) { return mlirBoolAttrGetValue(self.attr); },
+ [](PyBoolAttribute &self) { return mlirBoolAttrGetValue(self); },
"Returns the value of the bool attribute");
}
};
"get_typed",
[](PyType &type, std::string value) {
MlirAttribute attr =
- mlirStringAttrTypedGet(type.type, value.size(), &value[0]);
+ mlirStringAttrTypedGet(type, value.size(), &value[0]);
return PyStringAttribute(type.getContext(), attr);
},
c.def_property_readonly(
"value",
[](PyStringAttribute &self) {
- MlirStringRef stringRef = mlirStringAttrGetValue(self.attr);
+ MlirStringRef stringRef = mlirStringAttrGetValue(self);
return py::str(stringRef.data, stringRef.length);
},
"Returns the value of the string attribute");
PyAttribute &elementAttr) {
auto contextWrapper =
PyMlirContext::forContext(mlirTypeGetContext(shapedType));
- if (!mlirAttributeIsAInteger(elementAttr.attr) &&
- !mlirAttributeIsAFloat(elementAttr.attr)) {
+ if (!mlirAttributeIsAInteger(elementAttr) &&
+ !mlirAttributeIsAFloat(elementAttr)) {
std::string message = "Illegal element type for DenseElementsAttr: ";
message.append(py::repr(py::cast(elementAttr)));
throw SetPyError(PyExc_ValueError, message);
message.append(py::repr(py::cast(shapedType)));
throw SetPyError(PyExc_ValueError, message);
}
- MlirType shapedElementType = mlirShapedTypeGetElementType(shapedType.type);
- MlirType attrType = mlirAttributeGetType(elementAttr.attr);
+ MlirType shapedElementType = mlirShapedTypeGetElementType(shapedType);
+ MlirType attrType = mlirAttributeGetType(elementAttr);
if (!mlirTypeEqual(shapedElementType, attrType)) {
std::string message =
"Shaped element type and attribute type must be equal: shaped=";
}
MlirAttribute elements =
- mlirDenseElementsAttrSplatGet(shapedType.type, elementAttr.attr);
+ mlirDenseElementsAttrSplatGet(shapedType, elementAttr);
return PyDenseElementsAttribute(contextWrapper->getRef(), elements);
}
- intptr_t dunderLen() { return mlirElementsAttrGetNumElements(attr); }
+ intptr_t dunderLen() { return mlirElementsAttrGetNumElements(*this); }
py::buffer_info accessBuffer() {
- MlirType shapedType = mlirAttributeGetType(this->attr);
+ MlirType shapedType = mlirAttributeGetType(*this);
MlirType elementType = mlirShapedTypeGetElementType(shapedType);
if (mlirTypeIsAF32(elementType)) {
"Gets a DenseElementsAttr where all values are the same")
.def_property_readonly("is_splat",
[](PyDenseElementsAttribute &self) -> bool {
- return mlirDenseElementsAttrIsSplat(self.attr);
+ return mlirDenseElementsAttrIsSplat(self);
})
.def_buffer(&PyDenseElementsAttribute::accessBuffer);
}
// Prepare the data for the buffer_info.
// Buffer is configured for read-only access below.
Type *data = static_cast<Type *>(
- const_cast<void *>(mlirDenseElementsAttrGetRawData(this->attr)));
+ const_cast<void *>(mlirDenseElementsAttrGetRawData(*this)));
// Prepare the shape for the buffer_info.
SmallVector<intptr_t, 4> shape;
for (intptr_t i = 0; i < rank; ++i)
"attempt to access out of bounds element");
}
- MlirType type = mlirAttributeGetType(attr);
+ MlirType type = mlirAttributeGetType(*this);
type = mlirShapedTypeGetElementType(type);
assert(mlirTypeIsAInteger(type) &&
"expected integer element type in dense int elements attribute");
bool isUnsigned = mlirIntegerTypeIsUnsigned(type);
if (isUnsigned) {
if (width == 1) {
- return mlirDenseElementsAttrGetBoolValue(attr, pos);
+ return mlirDenseElementsAttrGetBoolValue(*this, pos);
}
if (width == 32) {
- return mlirDenseElementsAttrGetUInt32Value(attr, pos);
+ return mlirDenseElementsAttrGetUInt32Value(*this, pos);
}
if (width == 64) {
- return mlirDenseElementsAttrGetUInt64Value(attr, pos);
+ return mlirDenseElementsAttrGetUInt64Value(*this, pos);
}
} else {
if (width == 1) {
- return mlirDenseElementsAttrGetBoolValue(attr, pos);
+ return mlirDenseElementsAttrGetBoolValue(*this, pos);
}
if (width == 32) {
- return mlirDenseElementsAttrGetInt32Value(attr, pos);
+ return mlirDenseElementsAttrGetInt32Value(*this, pos);
}
if (width == 64) {
- return mlirDenseElementsAttrGetInt64Value(attr, pos);
+ return mlirDenseElementsAttrGetInt64Value(*this, pos);
}
}
throw SetPyError(PyExc_TypeError, "Unsupported integer type");
"attempt to access out of bounds element");
}
- MlirType type = mlirAttributeGetType(attr);
+ MlirType type = mlirAttributeGetType(*this);
type = mlirShapedTypeGetElementType(type);
// Dispatch element extraction to an appropriate C function based on the
// elemental type of the attribute. py::float_ is implicitly constructible
// TODO: consider caching the type properties in the constructor to avoid
// querying them on each element access.
if (mlirTypeIsAF32(type)) {
- return mlirDenseElementsAttrGetFloatValue(attr, pos);
+ return mlirDenseElementsAttrGetFloatValue(*this, pos);
}
if (mlirTypeIsAF64(type)) {
- return mlirDenseElementsAttrGetDoubleValue(attr, pos);
+ return mlirDenseElementsAttrGetDoubleValue(*this, pos);
}
throw SetPyError(PyExc_TypeError, "Unsupported floating-point type");
}
: PyConcreteType(orig.getContext(), castFrom(orig)) {}
static MlirType castFrom(PyType &orig) {
- if (!DerivedTy::isaFunction(orig.type)) {
+ if (!DerivedTy::isaFunction(orig)) {
auto origRepr = py::repr(py::cast(orig)).cast<std::string>();
throw SetPyError(PyExc_ValueError, llvm::Twine("Cannot cast type to ") +
DerivedTy::pyClassName +
" (from " + origRepr + ")");
}
- return orig.type;
+ return orig;
}
static void bind(py::module &m) {
"Create an unsigned integer type");
c.def_property_readonly(
"width",
- [](PyIntegerType &self) { return mlirIntegerTypeGetWidth(self.type); },
+ [](PyIntegerType &self) { return mlirIntegerTypeGetWidth(self); },
"Returns the width of the integer type");
c.def_property_readonly(
"is_signless",
[](PyIntegerType &self) -> bool {
- return mlirIntegerTypeIsSignless(self.type);
+ return mlirIntegerTypeIsSignless(self);
},
"Returns whether this is a signless integer");
c.def_property_readonly(
"is_signed",
[](PyIntegerType &self) -> bool {
- return mlirIntegerTypeIsSigned(self.type);
+ return mlirIntegerTypeIsSigned(self);
},
"Returns whether this is a signed integer");
c.def_property_readonly(
"is_unsigned",
[](PyIntegerType &self) -> bool {
- return mlirIntegerTypeIsUnsigned(self.type);
+ return mlirIntegerTypeIsUnsigned(self);
},
"Returns whether this is an unsigned integer");
}
"get",
[](PyType &elementType) {
// The element must be a floating point or integer scalar type.
- if (mlirTypeIsAIntegerOrFloat(elementType.type)) {
- MlirType t = mlirComplexTypeGet(elementType.type);
+ if (mlirTypeIsAIntegerOrFloat(elementType)) {
+ MlirType t = mlirComplexTypeGet(elementType);
return PyComplexType(elementType.getContext(), t);
}
throw SetPyError(
c.def_property_readonly(
"element_type",
[](PyComplexType &self) -> PyType {
- MlirType t = mlirComplexTypeGetElementType(self.type);
+ MlirType t = mlirComplexTypeGetElementType(self);
return PyType(self.getContext(), t);
},
"Returns element type.");
c.def_property_readonly(
"element_type",
[](PyShapedType &self) {
- MlirType t = mlirShapedTypeGetElementType(self.type);
+ MlirType t = mlirShapedTypeGetElementType(self);
return PyType(self.getContext(), t);
},
"Returns the element type of the shaped type.");
c.def_property_readonly(
"has_rank",
- [](PyShapedType &self) -> bool {
- return mlirShapedTypeHasRank(self.type);
- },
+ [](PyShapedType &self) -> bool { return mlirShapedTypeHasRank(self); },
"Returns whether the given shaped type is ranked.");
c.def_property_readonly(
"rank",
[](PyShapedType &self) {
self.requireHasRank();
- return mlirShapedTypeGetRank(self.type);
+ return mlirShapedTypeGetRank(self);
},
"Returns the rank of the given ranked shaped type.");
c.def_property_readonly(
"has_static_shape",
[](PyShapedType &self) -> bool {
- return mlirShapedTypeHasStaticShape(self.type);
+ return mlirShapedTypeHasStaticShape(self);
},
"Returns whether the given shaped type has a static shape.");
c.def(
"is_dynamic_dim",
[](PyShapedType &self, intptr_t dim) -> bool {
self.requireHasRank();
- return mlirShapedTypeIsDynamicDim(self.type, dim);
+ return mlirShapedTypeIsDynamicDim(self, dim);
},
"Returns whether the dim-th dimension of the given shaped type is "
"dynamic.");
"get_dim_size",
[](PyShapedType &self, intptr_t dim) {
self.requireHasRank();
- return mlirShapedTypeGetDimSize(self.type, dim);
+ return mlirShapedTypeGetDimSize(self, dim);
},
"Returns the dim-th dimension of the given ranked shaped type.");
c.def_static(
private:
void requireHasRank() {
- if (!mlirShapedTypeHasRank(type)) {
+ if (!mlirShapedTypeHasRank(*this)) {
throw SetPyError(
PyExc_ValueError,
"calling this method requires that the type has a rank.");
[](std::vector<int64_t> shape, PyType &elementType,
DefaultingPyLocation loc) {
MlirType t = mlirVectorTypeGetChecked(shape.size(), shape.data(),
- elementType.type, loc->loc);
+ elementType, loc);
// TODO: Rework error reporting once diagnostic engine is exposed
// in C API.
if (mlirTypeIsNull(t)) {
[](std::vector<int64_t> shape, PyType &elementType,
DefaultingPyLocation loc) {
MlirType t = mlirRankedTensorTypeGetChecked(
- shape.size(), shape.data(), elementType.type, loc->loc);
+ shape.size(), shape.data(), elementType, loc);
// TODO: Rework error reporting once diagnostic engine is exposed
// in C API.
if (mlirTypeIsNull(t)) {
c.def_static(
"get",
[](PyType &elementType, DefaultingPyLocation loc) {
- MlirType t =
- mlirUnrankedTensorTypeGetChecked(elementType.type, loc->loc);
+ MlirType t = mlirUnrankedTensorTypeGetChecked(elementType, loc);
// TODO: Rework error reporting once diagnostic engine is exposed
// in C API.
if (mlirTypeIsNull(t)) {
[](PyType &elementType, std::vector<int64_t> shape,
unsigned memorySpace, DefaultingPyLocation loc) {
MlirType t = mlirMemRefTypeContiguousGetChecked(
- elementType.type, shape.size(), shape.data(), memorySpace,
- loc->loc);
+ elementType, shape.size(), shape.data(), memorySpace, loc);
// TODO: Rework error reporting once diagnostic engine is exposed
// in C API.
if (mlirTypeIsNull(t)) {
.def_property_readonly(
"num_affine_maps",
[](PyMemRefType &self) -> intptr_t {
- return mlirMemRefTypeGetNumAffineMaps(self.type);
+ return mlirMemRefTypeGetNumAffineMaps(self);
},
"Returns the number of affine layout maps in the given MemRef "
"type.")
.def_property_readonly(
"memory_space",
[](PyMemRefType &self) -> unsigned {
- return mlirMemRefTypeGetMemorySpace(self.type);
+ return mlirMemRefTypeGetMemorySpace(self);
},
"Returns the memory space of the given MemRef type.");
}
"get",
[](PyType &elementType, unsigned memorySpace,
DefaultingPyLocation loc) {
- MlirType t = mlirUnrankedMemRefTypeGetChecked(elementType.type,
- memorySpace, loc->loc);
+ MlirType t =
+ mlirUnrankedMemRefTypeGetChecked(elementType, memorySpace, loc);
// TODO: Rework error reporting once diagnostic engine is exposed
// in C API.
if (mlirTypeIsNull(t)) {
.def_property_readonly(
"memory_space",
[](PyUnrankedMemRefType &self) -> unsigned {
- return mlirUnrankedMemrefGetMemorySpace(self.type);
+ return mlirUnrankedMemrefGetMemorySpace(self);
},
"Returns the memory space of the given Unranked MemRef type.");
}
// Mapping py::list to SmallVector.
SmallVector<MlirType, 4> elements;
for (auto element : elementList)
- elements.push_back(element.cast<PyType>().type);
+ elements.push_back(element.cast<PyType>());
MlirType t = mlirTupleTypeGet(context->get(), num, elements.data());
return PyTupleType(context->getRef(), t);
},
c.def(
"get_type",
[](PyTupleType &self, intptr_t pos) -> PyType {
- MlirType t = mlirTupleTypeGetType(self.type, pos);
+ MlirType t = mlirTupleTypeGetType(self, pos);
return PyType(self.getContext(), t);
},
"Returns the pos-th type in the tuple type.");
c.def_property_readonly(
"num_types",
[](PyTupleType &self) -> intptr_t {
- return mlirTupleTypeGetNumTypes(self.type);
+ return mlirTupleTypeGetNumTypes(self);
},
"Returns the number of types contained in a tuple.");
}
c.def_property_readonly(
"inputs",
[](PyFunctionType &self) {
- MlirType t = self.type;
+ MlirType t = self;
auto contextRef = self.getContext();
py::list types;
- for (intptr_t i = 0, e = mlirFunctionTypeGetNumInputs(self.type);
- i < e; ++i) {
+ for (intptr_t i = 0, e = mlirFunctionTypeGetNumInputs(self); i < e;
+ ++i) {
types.append(PyType(contextRef, mlirFunctionTypeGetInput(t, i)));
}
return types;
c.def_property_readonly(
"results",
[](PyFunctionType &self) {
- MlirType t = self.type;
auto contextRef = self.getContext();
py::list types;
- for (intptr_t i = 0, e = mlirFunctionTypeGetNumResults(self.type);
- i < e; ++i) {
- types.append(PyType(contextRef, mlirFunctionTypeGetResult(t, i)));
+ for (intptr_t i = 0, e = mlirFunctionTypeGetNumResults(self); i < e;
+ ++i) {
+ types.append(
+ PyType(contextRef, mlirFunctionTypeGetResult(self, i)));
}
return types;
},
// Mapping of Location
//----------------------------------------------------------------------------
py::class_<PyLocation>(m, "Location")
+ .def_property_readonly(MLIR_PYTHON_CAPI_PTR_ATTR, &PyLocation::getCapsule)
+ .def(MLIR_PYTHON_CAPI_FACTORY_ATTR, &PyLocation::createFromCapsule)
.def("__enter__", &PyLocation::contextEnter)
.def("__exit__", &PyLocation::contextExit)
+ .def("__eq__",
+ [](PyLocation &self, PyLocation &other) -> bool {
+ return mlirLocationEqual(self, other);
+ })
+ .def("__eq__", [](PyLocation &self, py::object other) { return false; })
.def_property_readonly_static(
"current",
[](py::object & /*class*/) {
"Context that owns the Location")
.def("__repr__", [](PyLocation &self) {
PyPrintAccumulator printAccum;
- mlirLocationPrint(self.loc, printAccum.getCallback(),
+ mlirLocationPrint(self, printAccum.getCallback(),
printAccum.getUserData());
return printAccum.join();
});
.def_static(
"create",
[](DefaultingPyLocation loc) {
- MlirModule module = mlirModuleCreateEmpty(loc->loc);
+ MlirModule module = mlirModuleCreateEmpty(loc);
return PyModule::forModule(module).releaseObject();
},
py::arg("loc") = py::none(), "Creates an empty module")
// Mapping of PyAttribute.
//----------------------------------------------------------------------------
py::class_<PyAttribute>(m, "Attribute")
+ .def_property_readonly(MLIR_PYTHON_CAPI_PTR_ATTR,
+ &PyAttribute::getCapsule)
+ .def(MLIR_PYTHON_CAPI_FACTORY_ATTR, &PyAttribute::createFromCapsule)
.def_static(
"parse",
[](std::string attrSpec, DefaultingPyMlirContext context) {
.def_property_readonly("type",
[](PyAttribute &self) {
return PyType(self.getContext()->getRef(),
- mlirAttributeGetType(self.attr));
+ mlirAttributeGetType(self));
})
.def(
"get_named",
[](PyAttribute &self, std::string name) {
- return PyNamedAttribute(self.attr, std::move(name));
+ return PyNamedAttribute(self, std::move(name));
},
py::keep_alive<0, 1>(), "Binds a name to the attribute")
.def("__eq__",
[](PyAttribute &self, PyAttribute &other) { return self == other; })
.def("__eq__", [](PyAttribute &self, py::object &other) { return false; })
.def(
- "dump", [](PyAttribute &self) { mlirAttributeDump(self.attr); },
+ "dump", [](PyAttribute &self) { mlirAttributeDump(self); },
kDumpDocstring)
.def(
"__str__",
[](PyAttribute &self) {
PyPrintAccumulator printAccum;
- mlirAttributePrint(self.attr, printAccum.getCallback(),
+ mlirAttributePrint(self, printAccum.getCallback(),
printAccum.getUserData());
return printAccum.join();
},
// being excessive.
PyPrintAccumulator printAccum;
printAccum.parts.append("Attribute(");
- mlirAttributePrint(self.attr, printAccum.getCallback(),
+ mlirAttributePrint(self, printAccum.getCallback(),
printAccum.getUserData());
printAccum.parts.append(")");
return printAccum.join();
// Mapping of PyType.
//----------------------------------------------------------------------------
py::class_<PyType>(m, "Type")
+ .def_property_readonly(MLIR_PYTHON_CAPI_PTR_ATTR, &PyType::getCapsule)
+ .def(MLIR_PYTHON_CAPI_FACTORY_ATTR, &PyType::createFromCapsule)
.def_static(
"parse",
[](std::string typeSpec, DefaultingPyMlirContext context) {
.def("__eq__", [](PyType &self, PyType &other) { return self == other; })
.def("__eq__", [](PyType &self, py::object &other) { return false; })
.def(
- "dump", [](PyType &self) { mlirTypeDump(self.type); }, kDumpDocstring)
+ "dump", [](PyType &self) { mlirTypeDump(self); }, kDumpDocstring)
.def(
"__str__",
[](PyType &self) {
PyPrintAccumulator printAccum;
- mlirTypePrint(self.type, printAccum.getCallback(),
+ mlirTypePrint(self, printAccum.getCallback(),
printAccum.getUserData());
return printAccum.join();
},
// assembly forms and printing them is useful.
PyPrintAccumulator printAccum;
printAccum.parts.append("Type(");
- mlirTypePrint(self.type, printAccum.getCallback(),
- printAccum.getUserData());
+ mlirTypePrint(self, printAccum.getCallback(), printAccum.getUserData());
printAccum.parts.append(")");
return printAccum.join();
});