Use local aliases for char boxing and unboxing in JS BE

This commit is contained in:
Alexey Andreev
2017-07-17 16:01:31 +03:00
parent 7646c2fc72
commit 1260146d25
20 changed files with 137 additions and 67 deletions
@@ -203,6 +203,14 @@ public final class DebugJsAstProtoBuf {
* <code>WRAP_FUNCTION = 2;</code>
*/
WRAP_FUNCTION(1, 2),
/**
* <code>TO_BOXED_CHAR = 3;</code>
*/
TO_BOXED_CHAR(2, 3),
/**
* <code>UNBOX_CHAR = 4;</code>
*/
UNBOX_CHAR(3, 4),
;
/**
@@ -213,6 +221,14 @@ public final class DebugJsAstProtoBuf {
* <code>WRAP_FUNCTION = 2;</code>
*/
public static final int WRAP_FUNCTION_VALUE = 2;
/**
* <code>TO_BOXED_CHAR = 3;</code>
*/
public static final int TO_BOXED_CHAR_VALUE = 3;
/**
* <code>UNBOX_CHAR = 4;</code>
*/
public static final int UNBOX_CHAR_VALUE = 4;
public final int getNumber() { return value; }
@@ -221,6 +237,8 @@ public final class DebugJsAstProtoBuf {
switch (value) {
case 1: return DEFINE_INLINE_FUNCTION;
case 2: return WRAP_FUNCTION;
case 3: return TO_BOXED_CHAR;
case 4: return UNBOX_CHAR;
default: return null;
}
}
@@ -49343,9 +49361,10 @@ public final class DebugJsAstProtoBuf {
"t.Fragment*@\n\013SideEffects\022\021\n\rAFFECTS_STA" +
"TE\020\001\022\024\n\020DEPENDS_ON_STATE\020\002\022\010\n\004PURE\020\003*?\n\016" +
"InlineStrategy\022\017\n\013AS_FUNCTION\020\000\022\014\n\010IN_PL" +
"ACE\020\001\022\016\n\nNOT_INLINE\020\002*@\n\017SpecialFunction" +
"ACE\020\001\022\016\n\nNOT_INLINE\020\002*c\n\017SpecialFunction" +
"\022\032\n\026DEFINE_INLINE_FUNCTION\020\001\022\021\n\rWRAP_FUN" +
"CTION\020\002B\024B\022DebugJsAstProtoBuf"
"CTION\020\002\022\021\n\rTO_BOXED_CHAR\020\003\022\016\n\nUNBOX_CHAR" +
"\020\004B\024B\022DebugJsAstProtoBuf"
};
org.jetbrains.kotlin.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner =
new org.jetbrains.kotlin.protobuf.Descriptors.FileDescriptor. InternalDescriptorAssigner() {
@@ -139,5 +139,7 @@ enum class SideEffectKind {
enum class SpecialFunction(val suggestedName: String) {
DEFINE_INLINE_FUNCTION("defineInlineFunction"),
WRAP_FUNCTION("wrapFunction")
WRAP_FUNCTION("wrapFunction"),
TO_BOXED_CHAR("toBoxedChar"),
UNBOX_CHAR("unboxChar"),
}
+2
View File
@@ -433,6 +433,8 @@ message Name {
enum SpecialFunction {
DEFINE_INLINE_FUNCTION = 1;
WRAP_FUNCTION = 2;
TO_BOXED_CHAR = 3;
UNBOX_CHAR = 4;
}
@@ -520,6 +520,8 @@ class JsAstDeserializer(program: JsProgram, private val sourceRoots: Iterable<Fi
private fun map(specialFunction: JsAstProtoBuf.SpecialFunction) = when(specialFunction) {
JsAstProtoBuf.SpecialFunction.DEFINE_INLINE_FUNCTION -> SpecialFunction.DEFINE_INLINE_FUNCTION
JsAstProtoBuf.SpecialFunction.WRAP_FUNCTION -> SpecialFunction.WRAP_FUNCTION
JsAstProtoBuf.SpecialFunction.TO_BOXED_CHAR -> SpecialFunction.TO_BOXED_CHAR
JsAstProtoBuf.SpecialFunction.UNBOX_CHAR -> SpecialFunction.UNBOX_CHAR
}
private fun <T : JsNode> withLocation(fileId: Int?, location: Location?, action: () -> T): T {
@@ -151,6 +151,14 @@ public final class JsAstProtoBuf {
* <code>WRAP_FUNCTION = 2;</code>
*/
WRAP_FUNCTION(1, 2),
/**
* <code>TO_BOXED_CHAR = 3;</code>
*/
TO_BOXED_CHAR(2, 3),
/**
* <code>UNBOX_CHAR = 4;</code>
*/
UNBOX_CHAR(3, 4),
;
/**
@@ -161,6 +169,14 @@ public final class JsAstProtoBuf {
* <code>WRAP_FUNCTION = 2;</code>
*/
public static final int WRAP_FUNCTION_VALUE = 2;
/**
* <code>TO_BOXED_CHAR = 3;</code>
*/
public static final int TO_BOXED_CHAR_VALUE = 3;
/**
* <code>UNBOX_CHAR = 4;</code>
*/
public static final int UNBOX_CHAR_VALUE = 4;
public final int getNumber() { return value; }
@@ -169,6 +185,8 @@ public final class JsAstProtoBuf {
switch (value) {
case 1: return DEFINE_INLINE_FUNCTION;
case 2: return WRAP_FUNCTION;
case 3: return TO_BOXED_CHAR;
case 4: return UNBOX_CHAR;
default: return null;
}
}
@@ -556,6 +556,8 @@ class JsAstSerializer(private val pathResolver: (File) -> String) {
private fun map(specialFunction: SpecialFunction) = when (specialFunction) {
SpecialFunction.DEFINE_INLINE_FUNCTION -> JsAstProtoBuf.SpecialFunction.DEFINE_INLINE_FUNCTION
SpecialFunction.WRAP_FUNCTION -> JsAstProtoBuf.SpecialFunction.WRAP_FUNCTION
SpecialFunction.TO_BOXED_CHAR -> JsAstProtoBuf.SpecialFunction.TO_BOXED_CHAR
SpecialFunction.UNBOX_CHAR -> JsAstProtoBuf.SpecialFunction.UNBOX_CHAR
}
private fun serialize(name: JsName): Int = nameMap.getOrPut(name) {
@@ -99,8 +99,8 @@ fun TranslationContext.getCallInfo(
return FunctionCallInfo(callInfo, argumentsInfo)
}
private fun boxIfNeedeed(v: ReceiverValue?, d: ReceiverParameterDescriptor?, r: JsExpression?): JsExpression? {
return r?.let { TranslationUtils.boxCastIfNeeded(it, v?.type, d?.type) }
private fun TranslationContext.boxIfNeedeed(v: ReceiverValue?, d: ReceiverParameterDescriptor?, r: JsExpression?): JsExpression? {
return r?.let { receiver -> TranslationUtils.boxCastIfNeeded(this, receiver, v?.type, d?.type) }
}
private fun TranslationContext.getDispatchReceiver(receiverValue: ReceiverValue): JsExpression {
@@ -353,12 +353,12 @@ public final class Namer {
}
@NotNull
public static JsNameRef createInlineFunction() {
private static JsNameRef createInlineFunction() {
return pureFqn(DEFINE_INLINE_FUNCTION, kotlinObject());
}
@NotNull
public static JsNameRef wrapFunction() {
private static JsNameRef wrapFunction() {
return pureFqn(WRAP_FUNCTION, kotlinObject());
}
@@ -369,6 +369,10 @@ public final class Namer {
return createInlineFunction();
case WRAP_FUNCTION:
return wrapFunction();
case TO_BOXED_CHAR:
return pureFqn("toBoxedChar", kotlinObject());
case UNBOX_CHAR:
return pureFqn("unboxChar", kotlinObject());
default:
throw new IllegalArgumentException("Unknown function: " + specialFunction);
}
@@ -84,7 +84,8 @@ public class DestructuringDeclarationTranslator extends AbstractTranslator {
setInlineCallMetadata(entryInitializer, entry, entryInitCall, context());
}
entryInitializer = TranslationUtils.boxCastIfNeeded(entryInitializer, candidateDescriptor.getReturnType(), descriptor.getType());
entryInitializer = TranslationUtils.boxCastIfNeeded(context(), entryInitializer, candidateDescriptor.getReturnType(),
descriptor.getType());
JsName name = context().getNameForDescriptor(descriptor);
if (isVarCapturedInClosure(context().bindingContext(), descriptor)) {
@@ -148,7 +148,7 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
if (KotlinBuiltIns.isCharOrNullableChar(returnedType) &&
TranslationUtils.shouldBoxReturnValue((CallableDescriptor)context.getDeclarationDescriptor())) {
jsReturnExpression = JsAstUtils.charToBoxedChar(jsReturnExpression);
jsReturnExpression = TranslationUtils.charToBoxedChar(context, jsReturnExpression);
}
jsReturn = new JsReturn(jsReturnExpression);
@@ -247,7 +247,7 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
JsExpression receiver = translateAsExpression(receiverExpression, context);
KotlinType type = context.bindingContext().getType(receiverExpression);
if (type != null && KotlinBuiltIns.isChar(type)) {
receiver = JsAstUtils.charToBoxedChar(receiver);
receiver = TranslationUtils.charToBoxedChar(context, receiver);
}
return new JsInvocation(context.namer().kotlin(GET_KCLASS_FROM_EXPRESSION), receiver);
}
@@ -83,7 +83,7 @@ public final class PatternTranslator extends AbstractTranslator {
KotlinType leftType = context().bindingContext().getType(left);
if (leftType != null && KotlinBuiltIns.isChar(leftType)) {
expressionToCast = JsAstUtils.charToBoxedChar(expressionToCast);
expressionToCast = TranslationUtils.charToBoxedChar(context(), expressionToCast);
}
TemporaryVariable temporary = context().declareTemporary(expressionToCast, expression);
@@ -104,7 +104,7 @@ public final class PatternTranslator extends AbstractTranslator {
KotlinType expressionType = context().bindingContext().getType(expression);
if (expressionType != null && KotlinBuiltIns.isCharOrNullableChar(expressionType)) {
result = JsAstUtils.boxedCharToChar(result);
result = TranslationUtils.boxedCharToChar(context(), result);
}
return result;
@@ -117,7 +117,7 @@ public final class PatternTranslator extends AbstractTranslator {
KotlinType leftType = context().bindingContext().getType(left);
if (leftType != null && KotlinBuiltIns.isChar(leftType)) {
expressionToCheck = JsAstUtils.charToBoxedChar(expressionToCheck);
expressionToCheck = TranslationUtils.charToBoxedChar(context(), expressionToCheck);
}
KtTypeReference typeReference = expression.getTypeReference();
@@ -42,6 +42,7 @@ import org.jetbrains.kotlin.js.translate.test.JSTestGenerator;
import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils;
import org.jetbrains.kotlin.js.translate.utils.BindingUtils;
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils;
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils;
import org.jetbrains.kotlin.js.translate.utils.mutator.AssignToExpressionMutator;
import org.jetbrains.kotlin.psi.KtDeclaration;
import org.jetbrains.kotlin.psi.KtExpression;
@@ -188,7 +189,8 @@ public final class Translation {
JsNode jsNode = translateExpression(expression, context, block);
if (jsNode instanceof JsExpression) {
KotlinType expressionType = context.bindingContext().getType(expression);
return unboxIfNeeded((JsExpression) jsNode, expressionType != null && KotlinBuiltIns.isCharOrNullableChar(expressionType));
return unboxIfNeeded(context, (JsExpression) jsNode,
expressionType != null && KotlinBuiltIns.isCharOrNullableChar(expressionType));
}
assert jsNode instanceof JsStatement : "Unexpected node of type: " + jsNode.getClass().toString();
@@ -204,11 +206,15 @@ public final class Translation {
}
@NotNull
public static JsExpression unboxIfNeeded(@NotNull JsExpression expression, boolean charOrNullableChar) {
public static JsExpression unboxIfNeeded(
@NotNull TranslationContext context,
@NotNull JsExpression expression,
boolean charOrNullableChar
) {
if (charOrNullableChar &&
(expression instanceof JsInvocation || expression instanceof JsNameRef || expression instanceof JsArrayAccess)
) {
expression = JsAstUtils.boxedCharToChar(expression);
expression = TranslationUtils.boxedCharToChar(context, expression);
}
return expression;
}
@@ -23,8 +23,8 @@ import org.jetbrains.kotlin.js.backend.ast.JsBinaryOperation
import org.jetbrains.kotlin.js.backend.ast.JsExpression
import org.jetbrains.kotlin.js.translate.context.TranslationContext
import org.jetbrains.kotlin.js.translate.operation.OperatorTable
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
import org.jetbrains.kotlin.js.translate.utils.PsiUtils
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtBinaryExpression
import org.jetbrains.kotlin.types.KotlinType
@@ -34,7 +34,7 @@ object AssignmentBOIF : BinaryOperationIntrinsicFactory {
private object CharAssignmentIntrinsic : AbstractBinaryOperationIntrinsic() {
override fun apply(expression: KtBinaryExpression, left: JsExpression, right: JsExpression, context: TranslationContext): JsExpression {
val operator = OperatorTable.getBinaryOperator(PsiUtils.getOperationToken(expression))
return JsBinaryOperation(operator, left, JsAstUtils.charToBoxedChar(right))
return JsBinaryOperation(operator, left, TranslationUtils.charToBoxedChar(context, right))
}
}
@@ -62,8 +62,8 @@ object EqualsBOIF : BinaryOperationIntrinsicFactory {
if (leftType != null && (leftType in SIMPLE_PRIMITIVES || leftType == rightType && leftType != PrimitiveType.LONG)) {
return JsBinaryOperation(if (isNegated) JsBinaryOperator.REF_NEQ else JsBinaryOperator.REF_EQ,
Translation.unboxIfNeeded(left, leftType == PrimitiveType.CHAR),
Translation.unboxIfNeeded(right, rightType == PrimitiveType.CHAR))
Translation.unboxIfNeeded(context, left, leftType == PrimitiveType.CHAR),
Translation.unboxIfNeeded(context, right, rightType == PrimitiveType.CHAR))
}
val resolvedCall = expression.getResolvedCall(context.bindingContext())
@@ -77,8 +77,8 @@ object EqualsBOIF : BinaryOperationIntrinsicFactory {
return JsBinaryOperation(if (isNegated) JsBinaryOperator.NEQ else JsBinaryOperator.EQ, left, right)
}
val maybeBoxedLeft = if (leftType == PrimitiveType.CHAR) JsAstUtils.charToBoxedChar(left) else left
val maybeBoxedRight = if (rightType == PrimitiveType.CHAR) JsAstUtils.charToBoxedChar(right) else right
val maybeBoxedLeft = if (leftType == PrimitiveType.CHAR) TranslationUtils.charToBoxedChar(context, left) else left
val maybeBoxedRight = if (rightType == PrimitiveType.CHAR) TranslationUtils.charToBoxedChar(context, right) else right
val result = TopLevelFIF.KOTLIN_EQUALS.apply(maybeBoxedLeft, Arrays.asList<JsExpression>(maybeBoxedRight), context)
return if (isNegated) JsAstUtils.not(result) else result
@@ -127,15 +127,14 @@ public final class BinaryOperationTranslator extends AbstractTranslator {
private JsExpression translateElvis() {
KotlinType expressionType = context().bindingContext().getType(expression);
JsExpression leftExpression = TranslationUtils.boxCastIfNeeded(Translation.translateAsExpression(leftKtExpression, context()),
context().bindingContext().getType(leftKtExpression),
expressionType);
JsExpression leftExpression = TranslationUtils.boxCastIfNeeded(
context(), Translation.translateAsExpression(leftKtExpression, context()),
context().bindingContext().getType(leftKtExpression), expressionType);
JsBlock rightBlock = new JsBlock();
JsExpression rightExpression =
TranslationUtils.boxCastIfNeeded(Translation.translateAsExpression(rightKtExpression, context(), rightBlock),
context().bindingContext().getType(rightKtExpression),
expressionType);
JsExpression rightExpression = TranslationUtils.boxCastIfNeeded(
context(),Translation.translateAsExpression(rightKtExpression, context(), rightBlock),
context().bindingContext().getType(rightKtExpression), expressionType);
if (rightBlock.isEmpty()) {
return TranslationUtils.notNullConditional(leftExpression, rightExpression, context());
@@ -67,7 +67,7 @@ public final class IntrinsicAssignmentTranslator extends AssignmentTranslator {
result = JsAstUtils.charToString(result);
}
else if (leftType == null || !KotlinBuiltIns.isCharOrNullableChar(leftType)) {
result = JsAstUtils.charToBoxedChar(result);
result = TranslationUtils.charToBoxedChar(context, result);
}
}
return result;
@@ -106,7 +106,7 @@ class CallArgumentTranslator private constructor(
if (hasSpreadOperator) {
if (isNativeFunctionCall) {
argsBeforeVararg = result
result = mutableListOf<JsExpression>()
result = mutableListOf()
concatArguments = prepareConcatArguments(arguments,
translateResolvedArgument(actualArgument, argsToJsExpr),
null)
@@ -196,7 +196,7 @@ class CallArgumentTranslator private constructor(
val argJs = Translation.translateAsExpression(parenthisedArgumentExpression, argumentContext)
arg to TranslationUtils.boxCastIfNeeded(argJs, argType, parameterType)
arg to TranslationUtils.boxCastIfNeeded(context, argJs, argType, parameterType)
}
val resolvedOrder = resolvedCall.valueArgumentsByIndex.orEmpty()
@@ -229,7 +229,7 @@ class CallArgumentTranslator private constructor(
val arguments = resolvedArgument.arguments
if (arguments.isEmpty()) {
return if (shouldWrapVarargInArray) {
return listOf(toArray(varargPrimitiveType, listOf<JsExpression>()))
return listOf(toArray(varargPrimitiveType, listOf()))
}
else {
listOf()
@@ -266,14 +266,14 @@ class CallArgumentTranslator private constructor(
var lastArrayContent = mutableListOf<JsExpression>()
val size = arguments.size
for (index in 0..size - 1) {
for (index in 0 until size) {
val valueArgument = arguments[index]
val expressionArgument = list[index]
if (valueArgument.getSpreadElement() != null) {
if (lastArrayContent.size > 0) {
concatArguments.add(toArray(varargPrimitiveType, lastArrayContent))
lastArrayContent = mutableListOf<JsExpression>()
lastArrayContent = mutableListOf()
}
concatArguments.add(expressionArgument)
}
@@ -149,7 +149,7 @@ public final class FunctionBodyTranslator extends AbstractTranslator {
KotlinType bodyType = context().bindingContext().getType(declaration.getBodyExpression());
if (bodyType == null && KotlinBuiltIns.isCharOrNullableChar(descriptor.getReturnType()) ||
bodyType != null && KotlinBuiltIns.isCharOrNullableChar(bodyType) && TranslationUtils.shouldBoxReturnValue(descriptor)) {
node = JsAstUtils.charToBoxedChar((JsExpression) node);
node = TranslationUtils.charToBoxedChar(context(), (JsExpression) node);
}
JsReturn jsReturn = new JsReturn((JsExpression) node);
@@ -157,34 +157,6 @@ public final class JsAstUtils {
return new JsInvocation(new JsNameRef("fromCharCode", new JsNameRef("String")), expression);
}
@NotNull
public static JsExpression charToBoxedChar(@NotNull JsExpression expression) {
JsInvocation invocation = invokeKotlinFunction("toBoxedChar", unnestBoxing(expression));
invocation.setSource(expression.getSource());
return withBoxingMetadata(invocation);
}
@NotNull
public static JsExpression boxedCharToChar(@NotNull JsExpression expression) {
JsInvocation invocation = invokeKotlinFunction("unboxChar", unnestBoxing(expression));
invocation.setSource(expression.getSource());
return withBoxingMetadata(invocation);
}
@NotNull
private static JsExpression unnestBoxing(@NotNull JsExpression expression) {
if (expression instanceof JsInvocation && MetadataProperties.getBoxing((JsInvocation) expression)) {
return ((JsInvocation) expression).getArguments().get(0);
}
return expression;
}
@NotNull
private static JsInvocation withBoxingMetadata(@NotNull JsInvocation call) {
MetadataProperties.setBoxing(call, true);
return call;
}
@NotNull
public static JsExpression toShort(@NotNull JsExpression expression) {
return invokeKotlinFunction(OperatorConventions.SHORT.getIdentifier(), expression);
@@ -25,6 +25,7 @@ import org.jetbrains.kotlin.descriptors.impl.LocalVariableAccessorDescriptor;
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor;
import org.jetbrains.kotlin.incremental.components.NoLookupLocation;
import org.jetbrains.kotlin.js.backend.ast.*;
import org.jetbrains.kotlin.js.backend.ast.metadata.MetadataProperties;
import org.jetbrains.kotlin.js.backend.ast.metadata.SpecialFunction;
import org.jetbrains.kotlin.js.translate.context.Namer;
import org.jetbrains.kotlin.js.translate.context.TemporaryConstVariable;
@@ -52,6 +53,7 @@ import static org.jetbrains.kotlin.js.backend.ast.JsBinaryOperator.*;
import static org.jetbrains.kotlin.js.translate.utils.BindingUtils.getCallableDescriptorForOperationExpression;
import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.assignment;
import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.createDataDescriptor;
import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.pureFqn;
public final class TranslationUtils {
@@ -206,7 +208,7 @@ public final class TranslationUtils {
KotlinType propertyType = BindingContextUtils.getNotNull(context.bindingContext(), BindingContext.VARIABLE, declaration).getType();
KotlinType initType = context.bindingContext().getType(initializer);
jsInitExpression = boxCastIfNeeded(jsInitExpression, initType, propertyType);
jsInitExpression = boxCastIfNeeded(context, jsInitExpression, initType, propertyType);
}
return jsInitExpression;
}
@@ -408,15 +410,56 @@ public final class TranslationUtils {
}
@NotNull
public static JsExpression boxCastIfNeeded(@NotNull JsExpression e, @Nullable KotlinType castFrom, @Nullable KotlinType castTo) {
public static JsExpression boxCastIfNeeded(
@NotNull TranslationContext context,
@NotNull JsExpression e,
@Nullable KotlinType castFrom, @Nullable KotlinType castTo
) {
if (castFrom != null && KotlinBuiltIns.isCharOrNullableChar(castFrom) &&
castTo != null && !KotlinBuiltIns.isCharOrNullableChar(castTo)
) {
return JsAstUtils.charToBoxedChar(e);
return charToBoxedChar(context, e);
}
return e;
}
@NotNull
public static JsExpression charToBoxedChar(@NotNull TranslationContext context, @NotNull JsExpression expression) {
JsInvocation invocation = invokeSpecialFunction(context, SpecialFunction.TO_BOXED_CHAR, unnestBoxing(expression));
invocation.setSource(expression.getSource());
return withBoxingMetadata(invocation);
}
@NotNull
public static JsExpression boxedCharToChar(@NotNull TranslationContext context, @NotNull JsExpression expression) {
JsInvocation invocation = invokeSpecialFunction(context, SpecialFunction.UNBOX_CHAR, unnestBoxing(expression));
invocation.setSource(expression.getSource());
return withBoxingMetadata(invocation);
}
@NotNull
private static JsExpression unnestBoxing(@NotNull JsExpression expression) {
if (expression instanceof JsInvocation && MetadataProperties.getBoxing((JsInvocation) expression)) {
return ((JsInvocation) expression).getArguments().get(0);
}
return expression;
}
@NotNull
private static JsInvocation withBoxingMetadata(@NotNull JsInvocation call) {
MetadataProperties.setBoxing(call, true);
return call;
}
@NotNull
private static JsInvocation invokeSpecialFunction(
@NotNull TranslationContext context,
@NotNull SpecialFunction function, @NotNull JsExpression... arguments
) {
JsName name = context.getNameForSpecialFunction(function);
return new JsInvocation(pureFqn(name, null), arguments);
}
@NotNull
public static String getTagForSpecialFunction(@NotNull SpecialFunction specialFunction) {
return "special:" + specialFunction.name();