Interface refs
This commit is contained in:
@@ -1,41 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010-2014 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 org.jetbrains.jet.codegen;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.org.objectweb.asm.Label;
|
|
||||||
import org.jetbrains.org.objectweb.asm.Type;
|
|
||||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter;
|
|
||||||
|
|
||||||
public interface IStackValue {
|
|
||||||
|
|
||||||
void moveToTopOfStack(@NotNull Type type, @NotNull InstructionAdapter v, int depth);
|
|
||||||
|
|
||||||
void put(@NotNull Type type, @NotNull InstructionAdapter v, boolean skipReceiver);
|
|
||||||
|
|
||||||
void store(@NotNull StackValue value, @NotNull InstructionAdapter v, boolean skipReceiver);
|
|
||||||
|
|
||||||
void condJump(@NotNull Label label, boolean jumpIfFalse, @NotNull InstructionAdapter v);
|
|
||||||
|
|
||||||
void coerceTo(@NotNull Type toType, @NotNull InstructionAdapter v);
|
|
||||||
|
|
||||||
void coerceFrom(@NotNull Type topOfStackType, @NotNull InstructionAdapter v);
|
|
||||||
|
|
||||||
void putAsBoolean(InstructionAdapter v);
|
|
||||||
|
|
||||||
void dup(@NotNull InstructionAdapter v, boolean withReceiver);
|
|
||||||
}
|
|
||||||
@@ -47,15 +47,12 @@ import static org.jetbrains.jet.codegen.AsmUtil.*;
|
|||||||
import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.*;
|
import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.*;
|
||||||
import static org.jetbrains.org.objectweb.asm.Opcodes.*;
|
import static org.jetbrains.org.objectweb.asm.Opcodes.*;
|
||||||
|
|
||||||
public abstract class StackValue implements IStackValue {
|
public abstract class StackValue {
|
||||||
|
|
||||||
private static final String NULLABLE_BYTE_TYPE_NAME = "java/lang/Byte";
|
private static final String NULLABLE_BYTE_TYPE_NAME = "java/lang/Byte";
|
||||||
private static final String NULLABLE_SHORT_TYPE_NAME = "java/lang/Short";
|
private static final String NULLABLE_SHORT_TYPE_NAME = "java/lang/Short";
|
||||||
private static final String NULLABLE_LONG_TYPE_NAME = "java/lang/Long";
|
private static final String NULLABLE_LONG_TYPE_NAME = "java/lang/Long";
|
||||||
|
|
||||||
public static final int RECEIVER_READ = 0;
|
|
||||||
public static final int RECEIVER_WRITE = 1;
|
|
||||||
|
|
||||||
public static final StackValue.Local LOCAL_0 = local(0, OBJECT_TYPE);
|
public static final StackValue.Local LOCAL_0 = local(0, OBJECT_TYPE);
|
||||||
private static final StackValue UNIT = operation(UNIT_TYPE, new Function1<InstructionAdapter, Unit>() {
|
private static final StackValue UNIT = operation(UNIT_TYPE, new Function1<InstructionAdapter, Unit>() {
|
||||||
@Override
|
@Override
|
||||||
@@ -80,7 +77,6 @@ public abstract class StackValue implements IStackValue {
|
|||||||
* @param v the visitor used to genClassOrObject the instructions
|
* @param v the visitor used to genClassOrObject the instructions
|
||||||
* @param depth the number of new values put onto the stack
|
* @param depth the number of new values put onto the stack
|
||||||
*/
|
*/
|
||||||
@Override
|
|
||||||
public void moveToTopOfStack(@NotNull Type type, @NotNull InstructionAdapter v, int depth) {
|
public void moveToTopOfStack(@NotNull Type type, @NotNull InstructionAdapter v, int depth) {
|
||||||
put(type, v);
|
put(type, v);
|
||||||
}
|
}
|
||||||
@@ -89,12 +85,21 @@ public abstract class StackValue implements IStackValue {
|
|||||||
put(type, v, false);
|
put(type, v, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void put(@NotNull Type type, @NotNull InstructionAdapter v, boolean skipReceiver) {
|
public void put(@NotNull Type type, @NotNull InstructionAdapter v, boolean skipReceiver) {
|
||||||
|
if (!skipReceiver) {
|
||||||
|
putReceiver(v, true);
|
||||||
|
}
|
||||||
putSelector(type, v);
|
putSelector(type, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public abstract void putSelector(@NotNull Type type, @NotNull InstructionAdapter v);
|
||||||
|
|
||||||
|
public boolean hasReceiver(boolean isRead) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putReceiver(@NotNull InstructionAdapter v, boolean isRead) {}
|
||||||
|
|
||||||
public void dup(@NotNull InstructionAdapter v, boolean withReceiver) {
|
public void dup(@NotNull InstructionAdapter v, boolean withReceiver) {
|
||||||
switch (type.getSize()) {
|
switch (type.getSize()) {
|
||||||
case 0: break;
|
case 0: break;
|
||||||
@@ -105,23 +110,22 @@ public abstract class StackValue implements IStackValue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void storeSelector(@NotNull Type topOfStackType, @NotNull InstructionAdapter v) {
|
|
||||||
throw new UnsupportedOperationException("cannot store to value " + this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract void putSelector(@NotNull Type type, @NotNull InstructionAdapter v);
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void store(@NotNull StackValue value, @NotNull InstructionAdapter v, boolean skipReceiver) {
|
|
||||||
value.put(value.type, v);
|
|
||||||
storeSelector(value.type, v);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void store(@NotNull StackValue value, @NotNull InstructionAdapter v) {
|
public void store(@NotNull StackValue value, @NotNull InstructionAdapter v) {
|
||||||
store(value, v, false);
|
store(value, v, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public void store(@NotNull StackValue value, @NotNull InstructionAdapter v, boolean skipReceiver) {
|
||||||
|
if (!skipReceiver) {
|
||||||
|
putReceiver(v, false);
|
||||||
|
}
|
||||||
|
value.put(value.type, v);
|
||||||
|
storeSelector(value.type, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void storeSelector(@NotNull Type topOfStackType, @NotNull InstructionAdapter v) {
|
||||||
|
throw new UnsupportedOperationException("cannot store to value " + this);
|
||||||
|
}
|
||||||
|
|
||||||
public void condJump(@NotNull Label label, boolean jumpIfFalse, @NotNull InstructionAdapter v) {
|
public void condJump(@NotNull Label label, boolean jumpIfFalse, @NotNull InstructionAdapter v) {
|
||||||
put(this.type, v);
|
put(this.type, v);
|
||||||
coerceTo(Type.BOOLEAN_TYPE, v);
|
coerceTo(Type.BOOLEAN_TYPE, v);
|
||||||
@@ -284,13 +288,11 @@ public abstract class StackValue implements IStackValue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
protected void coerceTo(@NotNull Type toType, @NotNull InstructionAdapter v) {
|
||||||
public void coerceTo(@NotNull Type toType, @NotNull InstructionAdapter v) {
|
|
||||||
coerce(this.type, toType, v);
|
coerce(this.type, toType, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
protected void coerceFrom(@NotNull Type topOfStackType, @NotNull InstructionAdapter v) {
|
||||||
public void coerceFrom(@NotNull Type topOfStackType, @NotNull InstructionAdapter v) {
|
|
||||||
coerce(topOfStackType, this.type, v);
|
coerce(topOfStackType, this.type, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -361,7 +363,6 @@ public abstract class StackValue implements IStackValue {
|
|||||||
return UNIT;
|
return UNIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void putAsBoolean(InstructionAdapter v) {
|
public void putAsBoolean(InstructionAdapter v) {
|
||||||
Label ifTrue = new Label();
|
Label ifTrue = new Label();
|
||||||
Label end = new Label();
|
Label end = new Label();
|
||||||
@@ -1321,7 +1322,7 @@ public abstract class StackValue implements IStackValue {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void putSelector(@NotNull Type type, @NotNull InstructionAdapter v) {
|
public void putSelector(@NotNull Type type, @NotNull InstructionAdapter v) {
|
||||||
value = StackValue.complexReceiver(value, RECEIVER_READ, RECEIVER_WRITE, RECEIVER_READ);
|
value = StackValue.complexReceiver(value, true, false, true);
|
||||||
value.put(this.type, v);
|
value.put(this.type, v);
|
||||||
|
|
||||||
if (callable instanceof CallableMethod) {
|
if (callable instanceof CallableMethod) {
|
||||||
@@ -1444,11 +1445,13 @@ public abstract class StackValue implements IStackValue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract static class StackValueWithSimpleReceiver extends StackValueWithReceiver {
|
public abstract static class StackValueWithSimpleReceiver extends StackValue {
|
||||||
|
|
||||||
public final boolean isStaticPut;
|
public final boolean isStaticPut;
|
||||||
|
|
||||||
public final boolean isStaticStore;
|
public final boolean isStaticStore;
|
||||||
|
@NotNull
|
||||||
|
public final StackValue receiver;
|
||||||
|
|
||||||
public StackValueWithSimpleReceiver(
|
public StackValueWithSimpleReceiver(
|
||||||
@NotNull Type type,
|
@NotNull Type type,
|
||||||
@@ -1456,20 +1459,12 @@ public abstract class StackValue implements IStackValue {
|
|||||||
boolean isStaticStore,
|
boolean isStaticStore,
|
||||||
@NotNull StackValue receiver
|
@NotNull StackValue receiver
|
||||||
) {
|
) {
|
||||||
super(type, receiver);
|
super(type);
|
||||||
|
this.receiver = receiver;
|
||||||
this.isStaticPut = isStaticPut;
|
this.isStaticPut = isStaticPut;
|
||||||
this.isStaticStore = isStaticStore;
|
this.isStaticStore = isStaticStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void put(@NotNull Type type, @NotNull InstructionAdapter v, boolean skipReceiver) {
|
|
||||||
if (!skipReceiver) {
|
|
||||||
putReceiver(v, true);
|
|
||||||
}
|
|
||||||
putSelector(type, v);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void putReceiver(@NotNull InstructionAdapter v, boolean isRead) {
|
public void putReceiver(@NotNull InstructionAdapter v, boolean isRead) {
|
||||||
if (hasReceiver(isRead)) {
|
if (hasReceiver(isRead)) {
|
||||||
@@ -1481,23 +1476,6 @@ public abstract class StackValue implements IStackValue {
|
|||||||
public boolean hasReceiver(boolean isRead) {
|
public boolean hasReceiver(boolean isRead) {
|
||||||
return isRead ? !isStaticPut : !isStaticStore;
|
return isRead ? !isStaticPut : !isStaticStore;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public abstract static class StackValueWithReceiver extends StackValue {
|
|
||||||
|
|
||||||
@NotNull public final StackValue receiver;
|
|
||||||
|
|
||||||
protected StackValueWithReceiver(@NotNull Type type, @NotNull StackValue receiver) {
|
|
||||||
super(type);
|
|
||||||
this.receiver = receiver;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public abstract void put(@NotNull Type type, @NotNull InstructionAdapter v, boolean skipReceiver);
|
|
||||||
|
|
||||||
public abstract void putReceiver(@NotNull InstructionAdapter v, boolean isRead);
|
|
||||||
|
|
||||||
public abstract boolean hasReceiver(boolean isRead);
|
|
||||||
|
|
||||||
public int receiverSize() {
|
public int receiverSize() {
|
||||||
return receiver.type.getSize();
|
return receiver.type.getSize();
|
||||||
@@ -1506,7 +1484,7 @@ public abstract class StackValue implements IStackValue {
|
|||||||
@Override
|
@Override
|
||||||
public void dup(@NotNull InstructionAdapter v, boolean withReceiver) {
|
public void dup(@NotNull InstructionAdapter v, boolean withReceiver) {
|
||||||
if (!withReceiver) {
|
if (!withReceiver) {
|
||||||
super.dup(v, false);
|
super.dup(v, withReceiver);
|
||||||
} else {
|
} else {
|
||||||
int receiverSize = hasReceiver(false) && hasReceiver(true) ? receiverSize() : 0;
|
int receiverSize = hasReceiver(false) && hasReceiver(true) ? receiverSize() : 0;
|
||||||
switch (receiverSize) {
|
switch (receiverSize) {
|
||||||
@@ -1552,13 +1530,13 @@ public abstract class StackValue implements IStackValue {
|
|||||||
|
|
||||||
static class ComplexReceiver extends StackValue {
|
static class ComplexReceiver extends StackValue {
|
||||||
|
|
||||||
private final StackValueWithReceiver originalValueWithReceiver;
|
private final StackValueWithSimpleReceiver originalValueWithReceiver;
|
||||||
private final int[] operations;
|
private final boolean [] isReadOperations;
|
||||||
|
|
||||||
public ComplexReceiver(StackValueWithReceiver value, int [] operations) {
|
public ComplexReceiver(StackValueWithSimpleReceiver value, boolean [] isReadOperations) {
|
||||||
super(value.type);
|
super(value.type);
|
||||||
this.originalValueWithReceiver = value;
|
this.originalValueWithReceiver = value;
|
||||||
this.operations = operations;
|
this.isReadOperations = isReadOperations;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -1566,8 +1544,8 @@ public abstract class StackValue implements IStackValue {
|
|||||||
@NotNull Type type, @NotNull InstructionAdapter v
|
@NotNull Type type, @NotNull InstructionAdapter v
|
||||||
) {
|
) {
|
||||||
boolean wasPutted = false;
|
boolean wasPutted = false;
|
||||||
for (int operation : operations) {
|
for (boolean operation : isReadOperations) {
|
||||||
if (originalValueWithReceiver.hasReceiver(operation == RECEIVER_READ)) {
|
if (originalValueWithReceiver.hasReceiver(operation)) {
|
||||||
StackValue receiver = originalValueWithReceiver.receiver;
|
StackValue receiver = originalValueWithReceiver.receiver;
|
||||||
if (!wasPutted) {
|
if (!wasPutted) {
|
||||||
receiver.put(receiver.type, v);
|
receiver.put(receiver.type, v);
|
||||||
@@ -1601,11 +1579,11 @@ public abstract class StackValue implements IStackValue {
|
|||||||
|
|
||||||
public static class Delegated extends StackValueWithSimpleReceiver {
|
public static class Delegated extends StackValueWithSimpleReceiver {
|
||||||
|
|
||||||
public final StackValueWithReceiver originalValue;
|
public final StackValueWithSimpleReceiver originalValue;
|
||||||
|
|
||||||
public Delegated(
|
public Delegated(
|
||||||
@NotNull Type type,
|
@NotNull Type type,
|
||||||
@NotNull StackValueWithReceiver originalValue,
|
@NotNull StackValueWithSimpleReceiver originalValue,
|
||||||
@NotNull StackValue receiver
|
@NotNull StackValue receiver
|
||||||
) {
|
) {
|
||||||
super(type, !originalValue.hasReceiver(true), !originalValue.hasReceiver(false), receiver);
|
super(type, !originalValue.hasReceiver(true), !originalValue.hasReceiver(false), receiver);
|
||||||
@@ -1633,13 +1611,13 @@ public abstract class StackValue implements IStackValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static StackValue complexWriteReadReceiver(StackValue stackValue) {
|
public static StackValue complexWriteReadReceiver(StackValue stackValue) {
|
||||||
return complexReceiver(stackValue, RECEIVER_WRITE, RECEIVER_READ);
|
return complexReceiver(stackValue, false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static StackValue complexReceiver(StackValue stackValue, int ... operations) {
|
private static StackValue complexReceiver(StackValue stackValue, boolean ... isReadOperations) {
|
||||||
if (stackValue instanceof StackValueWithReceiver) {
|
if (stackValue instanceof StackValueWithSimpleReceiver) {
|
||||||
return new Delegated(stackValue.type, (StackValueWithReceiver) stackValue,
|
return new Delegated(stackValue.type, (StackValueWithSimpleReceiver) stackValue,
|
||||||
new ComplexReceiver((StackValueWithSimpleReceiver) stackValue, operations));
|
new ComplexReceiver((StackValueWithSimpleReceiver) stackValue, isReadOperations));
|
||||||
} else {
|
} else {
|
||||||
return stackValue;
|
return stackValue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,30 +19,31 @@ package org.jetbrains.jet.codegen
|
|||||||
import org.jetbrains.org.objectweb.asm.Type
|
import org.jetbrains.org.objectweb.asm.Type
|
||||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||||
import org.jetbrains.org.objectweb.asm.Label
|
import org.jetbrains.org.objectweb.asm.Label
|
||||||
import org.jetbrains.jet.codegen.StackValue.StackValueWithReceiver
|
|
||||||
import org.jetbrains.jet.codegen.StackValue.StackValueWithSimpleReceiver
|
import org.jetbrains.jet.codegen.StackValue.StackValueWithSimpleReceiver
|
||||||
|
|
||||||
public fun coercion(value: StackValue, castType: Type): StackValue {
|
public fun coercion(value: StackValue, castType: Type): StackValue {
|
||||||
return if (value is StackValueWithReceiver) CoercionValueWithReceiver(value, castType) else CoercionValue(value, castType)
|
return CoercionValue(value, castType)
|
||||||
}
|
}
|
||||||
|
|
||||||
class CoercionValue(val value: StackValue, val castType: Type) : StackValue(castType), IStackValue by value {
|
class CoercionValue(
|
||||||
|
val value: StackValue,
|
||||||
|
val castType: Type
|
||||||
|
) : StackValue(castType) {
|
||||||
|
|
||||||
override fun putSelector(type: Type, v: InstructionAdapter) {
|
override fun putSelector(type: Type, v: InstructionAdapter) {
|
||||||
value.putSelector(type, v)
|
value.putSelector(type, v)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
class CoercionValueWithReceiver(
|
override fun storeSelector(topOfStackType: Type, v: InstructionAdapter) {
|
||||||
val value: StackValueWithReceiver,
|
value.storeSelector(topOfStackType, v)
|
||||||
val castType: Type
|
}
|
||||||
) : StackValueWithSimpleReceiver(castType, !value.hasReceiver(true), !value.hasReceiver(false), value.receiver), IStackValue by value {
|
|
||||||
|
|
||||||
override fun putReceiver(v: InstructionAdapter, isRead: Boolean) {
|
override fun putReceiver(v: InstructionAdapter, isRead: Boolean) {
|
||||||
value.putReceiver(v, isRead)
|
value.putReceiver(v, isRead)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun putSelector(type: Type, v: InstructionAdapter) {
|
override fun condJump(label: Label, jumpIfFalse: Boolean, v: InstructionAdapter) {
|
||||||
value.putSelector(type, v)
|
value.condJump(label, jumpIfFalse, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun hasReceiver(isRead: Boolean): Boolean {
|
override fun hasReceiver(isRead: Boolean): Boolean {
|
||||||
@@ -54,34 +55,26 @@ class CoercionValueWithReceiver(
|
|||||||
public class StackValueWithLeaveTask(
|
public class StackValueWithLeaveTask(
|
||||||
val stackValue: StackValue,
|
val stackValue: StackValue,
|
||||||
val leaveTasks: StackValueWithLeaveTask.() -> Unit
|
val leaveTasks: StackValueWithLeaveTask.() -> Unit
|
||||||
) : StackValueWithReceiver(
|
) : StackValue(stackValue.type) {
|
||||||
stackValue.type,
|
|
||||||
if (stackValue is StackValueWithReceiver) stackValue.receiver else StackValue.none()
|
|
||||||
), IStackValue by stackValue {
|
|
||||||
|
|
||||||
override fun put(type: Type, v: InstructionAdapter) {
|
override fun put(type: Type, v: InstructionAdapter, skipReceiver: Boolean) {
|
||||||
stackValue.put(type, v)
|
stackValue.put(type, v, skipReceiver)
|
||||||
leaveTasks()
|
leaveTasks()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun condJump(label: Label, jumpIfFalse: Boolean, v: InstructionAdapter ) {
|
override fun condJump(label: Label, jumpIfFalse: Boolean, v: InstructionAdapter) {
|
||||||
stackValue.condJump(label, jumpIfFalse, v)
|
stackValue.condJump(label, jumpIfFalse, v)
|
||||||
leaveTasks()
|
leaveTasks()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun putReceiver(v: InstructionAdapter, isRead: Boolean) {
|
override fun putReceiver(v: InstructionAdapter, isRead: Boolean) {
|
||||||
if (stackValue is StackValueWithReceiver) {
|
stackValue.putReceiver(v, isRead)
|
||||||
stackValue.putReceiver(v, isRead)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun putSelector(type: Type, v: InstructionAdapter) {
|
override fun putSelector(type: Type, v: InstructionAdapter) {
|
||||||
throw UnsupportedOperationException()
|
throw UnsupportedOperationException()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun hasReceiver(isRead: Boolean): Boolean {
|
|
||||||
throw UnsupportedOperationException()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun storeSelector(topOfStackType: Type, v: InstructionAdapter) {
|
override fun storeSelector(topOfStackType: Type, v: InstructionAdapter) {
|
||||||
throw UnsupportedOperationException();
|
throw UnsupportedOperationException();
|
||||||
|
|||||||
Reference in New Issue
Block a user