Removed obsolete code in codegen for 'var' parameters captured in closure. 'Var' parameters are forbidden now.
This commit is contained in:
@@ -51,8 +51,6 @@ public class CodegenUtil {
|
|||||||
private CodegenUtil() {
|
private CodegenUtil() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final Random RANDOM = new Random(55L);
|
|
||||||
|
|
||||||
public static boolean isInterface(DeclarationDescriptor descriptor) {
|
public static boolean isInterface(DeclarationDescriptor descriptor) {
|
||||||
if (descriptor instanceof ClassDescriptor) {
|
if (descriptor instanceof ClassDescriptor) {
|
||||||
ClassKind kind = ((ClassDescriptor) descriptor).getKind();
|
ClassKind kind = ((ClassDescriptor) descriptor).getKind();
|
||||||
@@ -85,18 +83,6 @@ public class CodegenUtil {
|
|||||||
return invokeDescriptor;
|
return invokeDescriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String createTmpVariableName(Collection<String> existingNames) {
|
|
||||||
String prefix = "tmp";
|
|
||||||
int i = RANDOM.nextInt(Integer.MAX_VALUE);
|
|
||||||
String name = prefix + i;
|
|
||||||
while (existingNames.contains(name)) {
|
|
||||||
i++;
|
|
||||||
name = prefix + i;
|
|
||||||
}
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static JvmMethodSignature erasedInvokeSignature(FunctionDescriptor fd) {
|
public static JvmMethodSignature erasedInvokeSignature(FunctionDescriptor fd) {
|
||||||
BothSignatureWriter signatureWriter = new BothSignatureWriter(BothSignatureWriter.Mode.METHOD, false);
|
BothSignatureWriter signatureWriter = new BothSignatureWriter(BothSignatureWriter.Mode.METHOD, false);
|
||||||
|
|
||||||
|
|||||||
@@ -99,7 +99,6 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
|
|||||||
private final CodegenStatementVisitor statementVisitor;
|
private final CodegenStatementVisitor statementVisitor;
|
||||||
|
|
||||||
private final Stack<BlockStackElement> blockStackElements = new Stack<BlockStackElement>();
|
private final Stack<BlockStackElement> blockStackElements = new Stack<BlockStackElement>();
|
||||||
private final Collection<String> localVariableNames = new HashSet<String>();
|
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private final MemberCodegen parentCodegen;
|
private final MemberCodegen parentCodegen;
|
||||||
@@ -176,13 +175,7 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
|
|||||||
this.returnType = returnType;
|
this.returnType = returnType;
|
||||||
this.state = state;
|
this.state = state;
|
||||||
this.methodVisitor = v;
|
this.methodVisitor = v;
|
||||||
this.v = new InstructionAdapter(methodVisitor) {
|
this.v = new InstructionAdapter(methodVisitor);
|
||||||
@Override
|
|
||||||
public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) {
|
|
||||||
super.visitLocalVariable(name, desc, signature, start, end, index);
|
|
||||||
localVariableNames.add(name);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
this.bindingContext = state.getBindingContext();
|
this.bindingContext = state.getBindingContext();
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.statementVisitor = new CodegenStatementVisitor(this);
|
this.statementVisitor = new CodegenStatementVisitor(this);
|
||||||
@@ -213,10 +206,6 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
|
|||||||
return bindingContext;
|
return bindingContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Collection<String> getLocalVariableNamesForExpression() {
|
|
||||||
return localVariableNames;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public MemberCodegen getParentCodegen() {
|
public MemberCodegen getParentCodegen() {
|
||||||
|
|||||||
@@ -42,7 +42,6 @@ import org.jetbrains.jet.lang.descriptors.*;
|
|||||||
import org.jetbrains.jet.lang.psi.JetNamedFunction;
|
import org.jetbrains.jet.lang.psi.JetNamedFunction;
|
||||||
import org.jetbrains.jet.lang.resolve.BindingContext;
|
import org.jetbrains.jet.lang.resolve.BindingContext;
|
||||||
import org.jetbrains.jet.lang.resolve.java.JvmAbi;
|
import org.jetbrains.jet.lang.resolve.java.JvmAbi;
|
||||||
import org.jetbrains.jet.lang.resolve.name.Name;
|
|
||||||
|
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
@@ -50,7 +49,8 @@ import java.util.*;
|
|||||||
|
|
||||||
import static org.jetbrains.asm4.Opcodes.*;
|
import static org.jetbrains.asm4.Opcodes.*;
|
||||||
import static org.jetbrains.jet.codegen.AsmUtil.*;
|
import static org.jetbrains.jet.codegen.AsmUtil.*;
|
||||||
import static org.jetbrains.jet.codegen.CodegenUtil.*;
|
import static org.jetbrains.jet.codegen.CodegenUtil.isCallInsideSameClassAsDeclared;
|
||||||
|
import static org.jetbrains.jet.codegen.CodegenUtil.isCallInsideSameModuleAsDeclared;
|
||||||
import static org.jetbrains.jet.codegen.JvmSerializationBindings.*;
|
import static org.jetbrains.jet.codegen.JvmSerializationBindings.*;
|
||||||
import static org.jetbrains.jet.codegen.binding.CodegenBinding.asmTypeForAnonymousClass;
|
import static org.jetbrains.jet.codegen.binding.CodegenBinding.asmTypeForAnonymousClass;
|
||||||
import static org.jetbrains.jet.codegen.binding.CodegenBinding.isLocalNamedFun;
|
import static org.jetbrains.jet.codegen.binding.CodegenBinding.isLocalNamedFun;
|
||||||
@@ -136,8 +136,6 @@ public class FunctionCodegen extends ParentCodegenAwareImpl {
|
|||||||
getThisTypeForFunction(functionDescriptor, methodContext),
|
getThisTypeForFunction(functionDescriptor, methodContext),
|
||||||
new Label(),
|
new Label(),
|
||||||
new Label(),
|
new Label(),
|
||||||
new HashSet<String>(getParameterNamesAsStrings(functionDescriptor)),
|
|
||||||
Collections.<Name, Label>emptyMap(),
|
|
||||||
methodContext.getContextKind()
|
methodContext.getContextKind()
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
@@ -265,10 +263,6 @@ public class FunctionCodegen extends ParentCodegenAwareImpl {
|
|||||||
@NotNull JvmMethodSignature signature,
|
@NotNull JvmMethodSignature signature,
|
||||||
@NotNull FunctionGenerationStrategy strategy
|
@NotNull FunctionGenerationStrategy strategy
|
||||||
) {
|
) {
|
||||||
Collection<String> localVariableNames = new HashSet<String>(getParameterNamesAsStrings(functionDescriptor));
|
|
||||||
|
|
||||||
Map<Name, Label> labelsForSharedVars = new HashMap<Name, Label>();
|
|
||||||
|
|
||||||
mv.visitCode();
|
mv.visitCode();
|
||||||
|
|
||||||
Label methodBegin = new Label();
|
Label methodBegin = new Label();
|
||||||
@@ -287,15 +281,12 @@ public class FunctionCodegen extends ParentCodegenAwareImpl {
|
|||||||
Label methodEntry = new Label();
|
Label methodEntry = new Label();
|
||||||
mv.visitLabel(methodEntry);
|
mv.visitLabel(methodEntry);
|
||||||
context.setMethodStartLabel(methodEntry);
|
context.setMethodStartLabel(methodEntry);
|
||||||
labelsForSharedVars.putAll(createSharedVarsForParameters(mv, functionDescriptor, frameMap));
|
|
||||||
|
|
||||||
if (!JetTypeMapper.isAccessor(functionDescriptor)) {
|
if (!JetTypeMapper.isAccessor(functionDescriptor)) {
|
||||||
genNotNullAssertionsForParameters(new InstructionAdapter(mv), state, functionDescriptor, frameMap);
|
genNotNullAssertionsForParameters(new InstructionAdapter(mv), state, functionDescriptor, frameMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
strategy.generateBody(mv, signature, context, getParentCodegen());
|
strategy.generateBody(mv, signature, context, getParentCodegen());
|
||||||
|
|
||||||
localVariableNames.addAll(strategy.getLocalVariableNames());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Label methodEnd = new Label();
|
Label methodEnd = new Label();
|
||||||
@@ -303,28 +294,16 @@ public class FunctionCodegen extends ParentCodegenAwareImpl {
|
|||||||
|
|
||||||
|
|
||||||
Type thisType = getThisTypeForFunction(functionDescriptor, context);
|
Type thisType = getThisTypeForFunction(functionDescriptor, context);
|
||||||
generateLocalVariableTable(mv, signature, functionDescriptor, thisType, methodBegin, methodEnd, localVariableNames, labelsForSharedVars, context.getContextKind());
|
generateLocalVariableTable(mv, signature, functionDescriptor, thisType, methodBegin, methodEnd, context.getContextKind());
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
private static void generateLocalVariableTable(
|
||||||
private static List<String> getParameterNamesAsStrings(@NotNull FunctionDescriptor functionDescriptor) {
|
|
||||||
List<ValueParameterDescriptor> parameters = functionDescriptor.getValueParameters();
|
|
||||||
List<String> result = new ArrayList<String>(parameters.size());
|
|
||||||
for (ValueParameterDescriptor parameter : parameters) {
|
|
||||||
result.add(parameter.getName().asString());
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void generateLocalVariableTable(
|
|
||||||
@NotNull MethodVisitor mv,
|
@NotNull MethodVisitor mv,
|
||||||
@NotNull JvmMethodSignature jvmMethodSignature,
|
@NotNull JvmMethodSignature jvmMethodSignature,
|
||||||
@NotNull FunctionDescriptor functionDescriptor,
|
@NotNull FunctionDescriptor functionDescriptor,
|
||||||
@Nullable Type thisType,
|
@Nullable Type thisType,
|
||||||
@NotNull Label methodBegin,
|
@NotNull Label methodBegin,
|
||||||
@NotNull Label methodEnd,
|
@NotNull Label methodEnd,
|
||||||
@NotNull Collection<String> localVariableNames,
|
|
||||||
@NotNull Map<Name, Label> labelsForSharedVars,
|
|
||||||
@NotNull OwnerKind ownerKind
|
@NotNull OwnerKind ownerKind
|
||||||
) {
|
) {
|
||||||
Iterator<ValueParameterDescriptor> valueParameters = functionDescriptor.getValueParameters().iterator();
|
Iterator<ValueParameterDescriptor> valueParameters = functionDescriptor.getValueParameters().iterator();
|
||||||
@@ -345,69 +324,25 @@ public class FunctionCodegen extends ParentCodegenAwareImpl {
|
|||||||
for (int i = 0; i < params.size(); i++) {
|
for (int i = 0; i < params.size(); i++) {
|
||||||
JvmMethodParameterSignature param = params.get(i);
|
JvmMethodParameterSignature param = params.get(i);
|
||||||
JvmMethodParameterKind kind = param.getKind();
|
JvmMethodParameterKind kind = param.getKind();
|
||||||
String parameterName = "$" + param.getKind().name().toLowerCase();
|
String parameterName;
|
||||||
|
|
||||||
if (needIndexForVar(kind)) {
|
if (kind == JvmMethodParameterKind.VALUE) {
|
||||||
parameterName = parameterName + "$" + i;
|
ValueParameterDescriptor parameter = valueParameters.next();
|
||||||
|
parameterName = parameter.getName().asString();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
String lowercaseKind = kind.name().toLowerCase();
|
||||||
|
parameterName = needIndexForVar(kind)
|
||||||
|
? "$" + lowercaseKind + "$" + i
|
||||||
|
: "$" + lowercaseKind;
|
||||||
}
|
}
|
||||||
|
|
||||||
Type type = param.getAsmType();
|
Type type = param.getAsmType();
|
||||||
if (kind == JvmMethodParameterKind.VALUE) {
|
|
||||||
ValueParameterDescriptor parameter = valueParameters.next();
|
|
||||||
Label divideLabel = labelsForSharedVars.get(parameter.getName());
|
|
||||||
parameterName = parameter.getName().asString();
|
|
||||||
if (divideLabel != null) {
|
|
||||||
mv.visitLocalVariable(parameterName, type.getDescriptor(), null, methodBegin, divideLabel, shift);
|
|
||||||
|
|
||||||
String nameForSharedVar = createTmpVariableName(localVariableNames);
|
|
||||||
localVariableNames.add(nameForSharedVar);
|
|
||||||
|
|
||||||
Type sharedVarType = typeMapper.getSharedVarType(parameter);
|
|
||||||
mv.visitLocalVariable(nameForSharedVar, sharedVarType.getDescriptor(), null, divideLabel, methodEnd, shift);
|
|
||||||
shift += Math.max(type.getSize(), sharedVarType.getSize());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mv.visitLocalVariable(parameterName, type.getDescriptor(), null, methodBegin, methodEnd, shift);
|
mv.visitLocalVariable(parameterName, type.getDescriptor(), null, methodBegin, methodEnd, shift);
|
||||||
shift += type.getSize();
|
shift += type.getSize();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private Map<Name, Label> createSharedVarsForParameters(
|
|
||||||
@NotNull MethodVisitor mv,
|
|
||||||
@NotNull FunctionDescriptor functionDescriptor,
|
|
||||||
@NotNull FrameMap frameMap
|
|
||||||
) {
|
|
||||||
Map<Name, Label> labelsForSharedVars = new HashMap<Name, Label>();
|
|
||||||
|
|
||||||
for (ValueParameterDescriptor parameter : functionDescriptor.getValueParameters()) {
|
|
||||||
Type sharedVarType = typeMapper.getSharedVarType(parameter);
|
|
||||||
if (sharedVarType == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Type localVarType = typeMapper.mapType(parameter);
|
|
||||||
int index = frameMap.getIndex(parameter);
|
|
||||||
mv.visitTypeInsn(NEW, sharedVarType.getInternalName());
|
|
||||||
mv.visitInsn(DUP);
|
|
||||||
mv.visitInsn(DUP);
|
|
||||||
mv.visitMethodInsn(INVOKESPECIAL, sharedVarType.getInternalName(), "<init>", "()V");
|
|
||||||
mv.visitVarInsn(localVarType.getOpcode(ILOAD), index);
|
|
||||||
mv.visitFieldInsn(PUTFIELD, sharedVarType.getInternalName(), "ref", StackValue.refType(localVarType).getDescriptor());
|
|
||||||
|
|
||||||
Label labelForSharedVar = new Label();
|
|
||||||
mv.visitLabel(labelForSharedVar);
|
|
||||||
|
|
||||||
labelsForSharedVars.put(parameter.getName(), labelForSharedVar);
|
|
||||||
|
|
||||||
mv.visitVarInsn(sharedVarType.getOpcode(ISTORE), index);
|
|
||||||
}
|
|
||||||
|
|
||||||
return labelsForSharedVars;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void generateStaticDelegateMethodBody(
|
private static void generateStaticDelegateMethodBody(
|
||||||
@NotNull MethodVisitor mv,
|
@NotNull MethodVisitor mv,
|
||||||
@NotNull Method asmMethod,
|
@NotNull Method asmMethod,
|
||||||
@@ -431,7 +366,7 @@ public class FunctionCodegen extends ParentCodegenAwareImpl {
|
|||||||
iv.areturn(asmMethod.getReturnType());
|
iv.areturn(asmMethod.getReturnType());
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean needIndexForVar(JvmMethodParameterKind kind) {
|
private static boolean needIndexForVar(JvmMethodParameterKind kind) {
|
||||||
return kind == JvmMethodParameterKind.SHARED_VAR || kind == JvmMethodParameterKind.SUPER_CALL_PARAM;
|
return kind == JvmMethodParameterKind.SHARED_VAR || kind == JvmMethodParameterKind.SUPER_CALL_PARAM;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -518,7 +453,7 @@ public class FunctionCodegen extends ParentCodegenAwareImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (Method overridden : bridgesToGenerate) {
|
for (Method overridden : bridgesToGenerate) {
|
||||||
generateBridge(owner, state, v, jvmSignature, functionDescriptor, overridden);
|
generateBridge(state, v, jvmSignature, functionDescriptor, overridden);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -637,14 +572,13 @@ public class FunctionCodegen extends ParentCodegenAwareImpl {
|
|||||||
Iterator<ValueParameterDescriptor> iterator = paramDescrs.iterator();
|
Iterator<ValueParameterDescriptor> iterator = paramDescrs.iterator();
|
||||||
|
|
||||||
int countOfExtraVarsInMethodArgs = 0;
|
int countOfExtraVarsInMethodArgs = 0;
|
||||||
List<JvmMethodParameterSignature> params = signature.getKotlinParameterTypes();
|
|
||||||
|
|
||||||
for (int i = 0; i < params.size(); i++) {
|
for (JvmMethodParameterSignature parameterSignature : signature.getKotlinParameterTypes()) {
|
||||||
JvmMethodParameterSignature parameterSignature = params.get(i);
|
|
||||||
if (parameterSignature.getKind() != JvmMethodParameterKind.VALUE) {
|
if (parameterSignature.getKind() != JvmMethodParameterKind.VALUE) {
|
||||||
countOfExtraVarsInMethodArgs++;
|
countOfExtraVarsInMethodArgs++;
|
||||||
frameMap.enterTemp(parameterSignature.getAsmType());
|
frameMap.enterTemp(parameterSignature.getAsmType());
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
frameMap.enter(iterator.next(), parameterSignature.getAsmType());
|
frameMap.enter(iterator.next(), parameterSignature.getAsmType());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -677,7 +611,7 @@ public class FunctionCodegen extends ParentCodegenAwareImpl {
|
|||||||
iv.load(frameMap.getIndex(parameterDescriptor), t);
|
iv.load(frameMap.getIndex(parameterDescriptor), t);
|
||||||
}
|
}
|
||||||
|
|
||||||
CallableMethod method = null;
|
CallableMethod method;
|
||||||
if (functionDescriptor instanceof ConstructorDescriptor) {
|
if (functionDescriptor instanceof ConstructorDescriptor) {
|
||||||
method = state.getTypeMapper().mapToCallableMethod((ConstructorDescriptor) functionDescriptor);
|
method = state.getTypeMapper().mapToCallableMethod((ConstructorDescriptor) functionDescriptor);
|
||||||
} else {
|
} else {
|
||||||
@@ -707,9 +641,7 @@ public class FunctionCodegen extends ParentCodegenAwareImpl {
|
|||||||
var += ownerType.getSize();
|
var += ownerType.getSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
List<JvmMethodParameterSignature> params = signature.getKotlinParameterTypes();
|
for (JvmMethodParameterSignature parameterSignature : signature.getKotlinParameterTypes()) {
|
||||||
for (int i = 0; i < params.size(); i++) {
|
|
||||||
JvmMethodParameterSignature parameterSignature = params.get(i);
|
|
||||||
if (parameterSignature.getKind() != JvmMethodParameterKind.VALUE) {
|
if (parameterSignature.getKind() != JvmMethodParameterKind.VALUE) {
|
||||||
Type type = parameterSignature.getAsmType();
|
Type type = parameterSignature.getAsmType();
|
||||||
iv.load(var, type);
|
iv.load(var, type);
|
||||||
@@ -767,7 +699,6 @@ public class FunctionCodegen extends ParentCodegenAwareImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void generateBridge(
|
private static void generateBridge(
|
||||||
CodegenContext owner,
|
|
||||||
GenerationState state,
|
GenerationState state,
|
||||||
ClassBuilder v,
|
ClassBuilder v,
|
||||||
Method jvmSignature,
|
Method jvmSignature,
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
public abstract class FunctionGenerationStrategy {
|
public abstract class FunctionGenerationStrategy {
|
||||||
private final Collection<String> localVariableNames = new ArrayList<String>();
|
|
||||||
private FrameMap frameMap;
|
private FrameMap frameMap;
|
||||||
|
|
||||||
public abstract void generateBody(
|
public abstract void generateBody(
|
||||||
@@ -40,15 +40,6 @@ public abstract class FunctionGenerationStrategy {
|
|||||||
@Nullable MemberCodegen parentCodegen
|
@Nullable MemberCodegen parentCodegen
|
||||||
);
|
);
|
||||||
|
|
||||||
protected void addLocalVariableName(@NotNull String name) {
|
|
||||||
localVariableNames.add(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public Collection<String> getLocalVariableNames() {
|
|
||||||
return localVariableNames;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
protected FrameMap createFrameMap(@NotNull JetTypeMapper typeMapper, @NotNull MethodContext context) {
|
protected FrameMap createFrameMap(@NotNull JetTypeMapper typeMapper, @NotNull MethodContext context) {
|
||||||
return context.prepareFrame(typeMapper);
|
return context.prepareFrame(typeMapper);
|
||||||
@@ -99,15 +90,8 @@ public abstract class FunctionGenerationStrategy {
|
|||||||
ExpressionCodegen codegen = new ExpressionCodegen(mv, getFrameMap(state.getTypeMapper(), context),
|
ExpressionCodegen codegen = new ExpressionCodegen(mv, getFrameMap(state.getTypeMapper(), context),
|
||||||
signature.getAsmMethod().getReturnType(), context, state, parentCodegen);
|
signature.getAsmMethod().getReturnType(), context, state, parentCodegen);
|
||||||
doGenerateBody(codegen, signature);
|
doGenerateBody(codegen, signature);
|
||||||
generateLocalVarNames(codegen);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract void doGenerateBody(@NotNull ExpressionCodegen codegen, @NotNull JvmMethodSignature signature);
|
public abstract void doGenerateBody(@NotNull ExpressionCodegen codegen, @NotNull JvmMethodSignature signature);
|
||||||
|
|
||||||
private void generateLocalVarNames(@NotNull ExpressionCodegen codegen) {
|
|
||||||
for (String name : codegen.getLocalVariableNamesForExpression()) {
|
|
||||||
addLocalVariableName(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user