throw exception when call-ing a system instruction.
This commit is contained in:
@@ -31,8 +31,8 @@ fn prolog_repl() {
|
|||||||
|
|
||||||
load_init_str_and_include(&mut wam, BUILTINS, "builtins");
|
load_init_str_and_include(&mut wam, BUILTINS, "builtins");
|
||||||
// load_init_str(&mut wam, LISTS);
|
// load_init_str(&mut wam, LISTS);
|
||||||
// load_init_str(&mut wam, CONTROL);
|
// load_init_str(&mut wam, CONTROL);
|
||||||
// load_init_str(&mut wam, QUEUES);
|
// load_init_str(&mut wam, QUEUES);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
print!("prolog> ");
|
print!("prolog> ");
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use prolog::builtins::*;
|
||||||
use prolog::num::bigint::BigInt;
|
use prolog::num::bigint::BigInt;
|
||||||
use prolog::num::{Float, ToPrimitive, Zero};
|
use prolog::num::{Float, ToPrimitive, Zero};
|
||||||
use prolog::num::rational::Ratio;
|
use prolog::num::rational::Ratio;
|
||||||
@@ -162,7 +163,7 @@ impl Module {
|
|||||||
pub fn new(module_decl: ModuleDecl) -> Self {
|
pub fn new(module_decl: ModuleDecl) -> Self {
|
||||||
Module { module_decl,
|
Module { module_decl,
|
||||||
code_dir: ModuleCodeDir::new(),
|
code_dir: ModuleCodeDir::new(),
|
||||||
op_dir: OpDir::new() }
|
op_dir: default_op_dir() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1367,8 +1368,9 @@ pub enum ArithmeticInstruction {
|
|||||||
Neg(ArithmeticTerm, usize)
|
Neg(ArithmeticTerm, usize)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// call and cut policy exempt instructions.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum BuiltInInstruction {
|
pub enum PEInstruction {
|
||||||
InstallCleaner,
|
InstallCleaner,
|
||||||
InstallInferenceCounter(RegType, RegType, RegType),
|
InstallInferenceCounter(RegType, RegType, RegType),
|
||||||
RemoveCallPolicyCheck,
|
RemoveCallPolicyCheck,
|
||||||
@@ -1451,7 +1453,7 @@ pub type CompiledQuery = Vec<QueryInstruction>;
|
|||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum Line {
|
pub enum Line {
|
||||||
Arithmetic(ArithmeticInstruction),
|
Arithmetic(ArithmeticInstruction),
|
||||||
BuiltIn(BuiltInInstruction),
|
PolicyExempt(PEInstruction),
|
||||||
Choice(ChoiceInstruction),
|
Choice(ChoiceInstruction),
|
||||||
Control(ControlInstruction),
|
Control(ControlInstruction),
|
||||||
Cut(CutInstruction),
|
Cut(CutInstruction),
|
||||||
|
|||||||
@@ -2,94 +2,6 @@ use prolog::ast::*;
|
|||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
// from 7.12.2 b) of 13211-1:1995
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub enum ValidType {
|
|
||||||
Atom,
|
|
||||||
Atomic,
|
|
||||||
Byte,
|
|
||||||
Callable,
|
|
||||||
Character,
|
|
||||||
Compound,
|
|
||||||
Evaluable,
|
|
||||||
InByte,
|
|
||||||
InCharacter,
|
|
||||||
Integer,
|
|
||||||
List,
|
|
||||||
Number,
|
|
||||||
Pair,
|
|
||||||
PredicateIndicator,
|
|
||||||
Variable
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ValidType {
|
|
||||||
pub fn as_str(self) -> &'static str {
|
|
||||||
match self {
|
|
||||||
ValidType::Atom => "atom",
|
|
||||||
ValidType::Atomic => "atomic",
|
|
||||||
ValidType::Byte => "byte",
|
|
||||||
ValidType::Callable => "callable",
|
|
||||||
ValidType::Character => "character",
|
|
||||||
ValidType::Compound => "compound",
|
|
||||||
ValidType::Evaluable => "evaluable",
|
|
||||||
ValidType::InByte => "in_byte",
|
|
||||||
ValidType::InCharacter => "in_character",
|
|
||||||
ValidType::Integer => "integer",
|
|
||||||
ValidType::List => "list",
|
|
||||||
ValidType::Number => "number",
|
|
||||||
ValidType::Pair => "pair",
|
|
||||||
ValidType::PredicateIndicator => "predicate_indicator",
|
|
||||||
ValidType::Variable => "variable"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// from 7.12.2 f) of 13211-1:1995
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub enum RepFlag {
|
|
||||||
Character,
|
|
||||||
CharacterCode,
|
|
||||||
InCharacterCode,
|
|
||||||
MaxArity,
|
|
||||||
MaxInteger,
|
|
||||||
MinInteger
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RepFlag {
|
|
||||||
pub fn as_str(self) -> &'static str {
|
|
||||||
match self {
|
|
||||||
RepFlag::Character => "character",
|
|
||||||
RepFlag::CharacterCode => "character_code",
|
|
||||||
RepFlag::InCharacterCode => "in_character_code",
|
|
||||||
RepFlag::MaxArity => "max_arity",
|
|
||||||
RepFlag::MaxInteger => "max_integer",
|
|
||||||
RepFlag::MinInteger => "min_integer"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// from 7.12.2 g) of 13211-1:1995
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub enum EvalError {
|
|
||||||
FloatOverflow,
|
|
||||||
IntOverflow,
|
|
||||||
Undefined,
|
|
||||||
Underflow,
|
|
||||||
ZeroDivisor
|
|
||||||
}
|
|
||||||
|
|
||||||
impl EvalError {
|
|
||||||
pub fn as_str(self) -> &'static str {
|
|
||||||
match self {
|
|
||||||
EvalError::FloatOverflow => "float_overflow",
|
|
||||||
EvalError::IntOverflow => "int_overflow",
|
|
||||||
EvalError::Undefined => "undefined",
|
|
||||||
EvalError::Underflow => "underflow",
|
|
||||||
EvalError::ZeroDivisor => "zero_divisor"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
fn get_builtins() -> Code {
|
fn get_builtins() -> Code {
|
||||||
vec![internal_call_n!(), // callN/N, 0.
|
vec![internal_call_n!(), // callN/N, 0.
|
||||||
|
|||||||
@@ -166,20 +166,20 @@ impl fmt::Display for IndexedChoiceInstruction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for BuiltInInstruction {
|
impl fmt::Display for PEInstruction {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
&BuiltInInstruction::InstallInferenceCounter(r1, r2, r3) =>
|
&PEInstruction::InstallInferenceCounter(r1, r2, r3) =>
|
||||||
write!(f, "install_inference_counter {}, {}, {}", r1, r2, r3),
|
write!(f, "install_inference_counter {}, {}, {}", r1, r2, r3),
|
||||||
&BuiltInInstruction::InstallCleaner =>
|
&PEInstruction::InstallCleaner =>
|
||||||
write!(f, "install_cleaner"),
|
write!(f, "install_cleaner"),
|
||||||
&BuiltInInstruction::RemoveCallPolicyCheck =>
|
&PEInstruction::RemoveCallPolicyCheck =>
|
||||||
write!(f, "remove_call_policy_check"),
|
write!(f, "remove_call_policy_check"),
|
||||||
&BuiltInInstruction::RemoveInferenceCounter(r1, r2) =>
|
&PEInstruction::RemoveInferenceCounter(r1, r2) =>
|
||||||
write!(f, "remove_inference_counter {}, {}", r1, r2),
|
write!(f, "remove_inference_counter {}, {}", r1, r2),
|
||||||
&BuiltInInstruction::RestoreCutPolicy =>
|
&PEInstruction::RestoreCutPolicy =>
|
||||||
write!(f, "restore_cut_point"),
|
write!(f, "restore_cut_point"),
|
||||||
&BuiltInInstruction::SetCutPoint(r) =>
|
&PEInstruction::SetCutPoint(r) =>
|
||||||
write!(f, "set_cp {}", r),
|
write!(f, "set_cp {}", r),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -328,7 +328,7 @@ pub fn print_code(code: &Code) {
|
|||||||
for fact_instr in fact {
|
for fact_instr in fact {
|
||||||
println!("{}", fact_instr);
|
println!("{}", fact_instr);
|
||||||
},
|
},
|
||||||
&Line::BuiltIn(ref instr) =>
|
&Line::PolicyExempt(ref instr) =>
|
||||||
println!("{}", instr),
|
println!("{}", instr),
|
||||||
&Line::Cut(ref cut) =>
|
&Line::Cut(ref cut) =>
|
||||||
println!("{}", cut),
|
println!("{}", cut),
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
use prolog::ast::*;
|
use prolog::ast::*;
|
||||||
use prolog::builtins::*;
|
|
||||||
use prolog::machine::machine_state::*;
|
use prolog::machine::machine_state::*;
|
||||||
use prolog::num::bigint::BigInt;
|
use prolog::num::bigint::BigInt;
|
||||||
|
|
||||||
@@ -8,6 +7,94 @@ use std::rc::Rc;
|
|||||||
pub(super) type MachineError = Vec<HeapCellValue>;
|
pub(super) type MachineError = Vec<HeapCellValue>;
|
||||||
pub(super) type MachineStub = Vec<HeapCellValue>;
|
pub(super) type MachineStub = Vec<HeapCellValue>;
|
||||||
|
|
||||||
|
// from 7.12.2 b) of 13211-1:1995
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum ValidType {
|
||||||
|
Atom,
|
||||||
|
Atomic,
|
||||||
|
Byte,
|
||||||
|
Callable,
|
||||||
|
Character,
|
||||||
|
Compound,
|
||||||
|
Evaluable,
|
||||||
|
InByte,
|
||||||
|
InCharacter,
|
||||||
|
Integer,
|
||||||
|
List,
|
||||||
|
Number,
|
||||||
|
Pair,
|
||||||
|
PredicateIndicator,
|
||||||
|
Variable
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ValidType {
|
||||||
|
pub fn as_str(self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
ValidType::Atom => "atom",
|
||||||
|
ValidType::Atomic => "atomic",
|
||||||
|
ValidType::Byte => "byte",
|
||||||
|
ValidType::Callable => "callable",
|
||||||
|
ValidType::Character => "character",
|
||||||
|
ValidType::Compound => "compound",
|
||||||
|
ValidType::Evaluable => "evaluable",
|
||||||
|
ValidType::InByte => "in_byte",
|
||||||
|
ValidType::InCharacter => "in_character",
|
||||||
|
ValidType::Integer => "integer",
|
||||||
|
ValidType::List => "list",
|
||||||
|
ValidType::Number => "number",
|
||||||
|
ValidType::Pair => "pair",
|
||||||
|
ValidType::PredicateIndicator => "predicate_indicator",
|
||||||
|
ValidType::Variable => "variable"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// from 7.12.2 f) of 13211-1:1995
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum RepFlag {
|
||||||
|
Character,
|
||||||
|
CharacterCode,
|
||||||
|
InCharacterCode,
|
||||||
|
MaxArity,
|
||||||
|
MaxInteger,
|
||||||
|
MinInteger
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RepFlag {
|
||||||
|
pub fn as_str(self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
RepFlag::Character => "character",
|
||||||
|
RepFlag::CharacterCode => "character_code",
|
||||||
|
RepFlag::InCharacterCode => "in_character_code",
|
||||||
|
RepFlag::MaxArity => "max_arity",
|
||||||
|
RepFlag::MaxInteger => "max_integer",
|
||||||
|
RepFlag::MinInteger => "min_integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// from 7.12.2 g) of 13211-1:1995
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum EvalError {
|
||||||
|
FloatOverflow,
|
||||||
|
IntOverflow,
|
||||||
|
Undefined,
|
||||||
|
Underflow,
|
||||||
|
ZeroDivisor
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EvalError {
|
||||||
|
pub fn as_str(self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
EvalError::FloatOverflow => "float_overflow",
|
||||||
|
EvalError::IntOverflow => "int_overflow",
|
||||||
|
EvalError::Undefined => "undefined",
|
||||||
|
EvalError::Underflow => "underflow",
|
||||||
|
EvalError::ZeroDivisor => "zero_divisor"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// used by '$skip_max_list'.
|
// used by '$skip_max_list'.
|
||||||
pub(super) enum CycleSearchResult {
|
pub(super) enum CycleSearchResult {
|
||||||
EmptyList,
|
EmptyList,
|
||||||
@@ -125,7 +212,7 @@ impl MachineState {
|
|||||||
let mut error_form = vec![HeapCellValue::NamedStr(2, clause_name!("error"), None),
|
let mut error_form = vec![HeapCellValue::NamedStr(2, clause_name!("error"), None),
|
||||||
HeapCellValue::Addr(Addr::HeapCell(h + 3)),
|
HeapCellValue::Addr(Addr::HeapCell(h + 3)),
|
||||||
HeapCellValue::Addr(Addr::HeapCell(h + 3 + err.len()))];
|
HeapCellValue::Addr(Addr::HeapCell(h + 3 + err.len()))];
|
||||||
|
|
||||||
error_form.extend(err.into_iter());
|
error_form.extend(err.into_iter());
|
||||||
error_form.extend(src.into_iter());
|
error_form.extend(src.into_iter());
|
||||||
|
|
||||||
@@ -143,6 +230,6 @@ impl MachineState {
|
|||||||
self.registers[1] = Addr::HeapCell(h);
|
self.registers[1] = Addr::HeapCell(h);
|
||||||
|
|
||||||
self.set_ball();
|
self.set_ball();
|
||||||
self.unwind_stack();
|
self.unwind_stack();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use prolog::and_stack::*;
|
|||||||
use prolog::ast::*;
|
use prolog::ast::*;
|
||||||
use prolog::copier::*;
|
use prolog::copier::*;
|
||||||
use prolog::heap_print::*;
|
use prolog::heap_print::*;
|
||||||
use prolog::machine::machine_errors::MachineStub;
|
use prolog::machine::machine_errors::*;
|
||||||
use prolog::num::{BigInt, BigUint, Zero, One};
|
use prolog::num::{BigInt, BigUint, Zero, One};
|
||||||
use prolog::or_stack::*;
|
use prolog::or_stack::*;
|
||||||
use prolog::tabled_rc::*;
|
use prolog::tabled_rc::*;
|
||||||
@@ -546,7 +546,8 @@ pub(crate) trait CallPolicy: Any {
|
|||||||
return Err(machine_st.existence_error(name, inner_arity));
|
return Err(machine_st.existence_error(name, inner_arity));
|
||||||
},
|
},
|
||||||
ClauseType::System(ct) =>
|
ClauseType::System(ct) =>
|
||||||
return machine_st.system_call(&ct)
|
return Err(machine_st.type_error(ValidType::Callable,
|
||||||
|
Addr::Con(Constant::Atom(name))))
|
||||||
};
|
};
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -1401,13 +1401,11 @@ impl MachineState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(super)
|
pub(super)
|
||||||
fn execute_built_in_instr<'a>(&mut self, code_dirs: CodeDirs<'a>,
|
fn execute_pe_instr<'a>(&mut self, code_dirs: CodeDirs<'a>, call_policy: &mut Box<CallPolicy>,
|
||||||
call_policy: &mut Box<CallPolicy>,
|
cut_policy: &mut Box<CutPolicy>, instr: &PEInstruction)
|
||||||
cut_policy: &mut Box<CutPolicy>,
|
|
||||||
instr: &BuiltInInstruction)
|
|
||||||
{
|
{
|
||||||
match instr {
|
match instr {
|
||||||
&BuiltInInstruction::InstallCleaner => {
|
&PEInstruction::InstallCleaner => {
|
||||||
let addr = self[temp_v!(1)].clone();
|
let addr = self[temp_v!(1)].clone();
|
||||||
let b = self.b;
|
let b = self.b;
|
||||||
let block = self.block;
|
let block = self.block;
|
||||||
@@ -1425,7 +1423,7 @@ impl MachineState {
|
|||||||
|
|
||||||
self.p += 1;
|
self.p += 1;
|
||||||
},
|
},
|
||||||
&BuiltInInstruction::InstallInferenceCounter(r1, r2, r3) => { // A1 = B, A2 = L
|
&PEInstruction::InstallInferenceCounter(r1, r2, r3) => { // A1 = B, A2 = L
|
||||||
let a1 = self.store(self.deref(self[r1].clone()));
|
let a1 = self.store(self.deref(self[r1].clone()));
|
||||||
let a2 = self.store(self.deref(self[r2].clone()));
|
let a2 = self.store(self.deref(self[r2].clone()));
|
||||||
|
|
||||||
@@ -1454,7 +1452,7 @@ impl MachineState {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
&BuiltInInstruction::RemoveCallPolicyCheck => {
|
&PEInstruction::RemoveCallPolicyCheck => {
|
||||||
let restore_default =
|
let restore_default =
|
||||||
match call_policy.downcast_mut::<CallWithInferenceLimitCallPolicy>().ok() {
|
match call_policy.downcast_mut::<CallWithInferenceLimitCallPolicy>().ok() {
|
||||||
Some(call_policy) => {
|
Some(call_policy) => {
|
||||||
@@ -1480,7 +1478,7 @@ impl MachineState {
|
|||||||
|
|
||||||
self.p += 1;
|
self.p += 1;
|
||||||
},
|
},
|
||||||
&BuiltInInstruction::RemoveInferenceCounter(r1, r2) => { // A1 = B
|
&PEInstruction::RemoveInferenceCounter(r1, r2) => { // A1 = B
|
||||||
match call_policy.downcast_mut::<CallWithInferenceLimitCallPolicy>().ok() {
|
match call_policy.downcast_mut::<CallWithInferenceLimitCallPolicy>().ok() {
|
||||||
Some(call_policy) => {
|
Some(call_policy) => {
|
||||||
let a1 = self.store(self.deref(self[r1].clone()));
|
let a1 = self.store(self.deref(self[r1].clone()));
|
||||||
@@ -1498,7 +1496,7 @@ impl MachineState {
|
|||||||
|
|
||||||
self.p += 1;
|
self.p += 1;
|
||||||
},
|
},
|
||||||
&BuiltInInstruction::RestoreCutPolicy => {
|
&PEInstruction::RestoreCutPolicy => {
|
||||||
let restore_default =
|
let restore_default =
|
||||||
if let Ok(cut_policy) = cut_policy.downcast_ref::<SetupCallCleanupCutPolicy>() {
|
if let Ok(cut_policy) = cut_policy.downcast_ref::<SetupCallCleanupCutPolicy>() {
|
||||||
cut_policy.out_of_cont_pts()
|
cut_policy.out_of_cont_pts()
|
||||||
@@ -1512,7 +1510,7 @@ impl MachineState {
|
|||||||
|
|
||||||
self.p += 1;
|
self.p += 1;
|
||||||
},
|
},
|
||||||
&BuiltInInstruction::SetCutPoint(r) =>
|
&PEInstruction::SetCutPoint(r) =>
|
||||||
cut_policy.cut(self, r),
|
cut_policy.cut(self, r),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -247,10 +247,10 @@ impl Machine {
|
|||||||
match instr {
|
match instr {
|
||||||
Line::Arithmetic(ref arith_instr) =>
|
Line::Arithmetic(ref arith_instr) =>
|
||||||
self.ms.execute_arith_instr(arith_instr),
|
self.ms.execute_arith_instr(arith_instr),
|
||||||
Line::BuiltIn(ref built_in_instr) => {
|
Line::PolicyExempt(ref built_in_instr) => {
|
||||||
let code_dirs = CodeDirs::new(&self.code_dir, &self.modules);
|
let code_dirs = CodeDirs::new(&self.code_dir, &self.modules);
|
||||||
self.ms.execute_built_in_instr(code_dirs, &mut self.call_policy,
|
self.ms.execute_pe_instr(code_dirs, &mut self.call_policy,
|
||||||
&mut self.cut_policy, built_in_instr);
|
&mut self.cut_policy, built_in_instr);
|
||||||
},
|
},
|
||||||
Line::Choice(ref choice_instr) =>
|
Line::Choice(ref choice_instr) =>
|
||||||
self.ms.execute_choice_instr(choice_instr, &mut self.call_policy),
|
self.ms.execute_choice_instr(choice_instr, &mut self.call_policy),
|
||||||
|
|||||||
@@ -165,10 +165,10 @@ impl MachineState {
|
|||||||
match a2 {
|
match a2 {
|
||||||
Addr::Con(Constant::Usize(bp)) =>
|
Addr::Con(Constant::Usize(bp)) =>
|
||||||
if self.b <= bp + 1 {
|
if self.b <= bp + 1 {
|
||||||
let a2 = Addr::Con(atom!("!", self.atom_tbl));
|
let a2 = Addr::Con(atom!("!"));
|
||||||
self.unify(a1, a2);
|
self.unify(a1, a2);
|
||||||
} else {
|
} else {
|
||||||
let a2 = Addr::Con(atom!("true", self.atom_tbl));
|
let a2 = Addr::Con(atom!("true"));
|
||||||
self.unify(a1, a2);
|
self.unify(a1, a2);
|
||||||
},
|
},
|
||||||
_ => self.fail = true
|
_ => self.fail = true
|
||||||
|
|||||||
@@ -152,7 +152,7 @@ macro_rules! is_call {
|
|||||||
|
|
||||||
macro_rules! set_cp {
|
macro_rules! set_cp {
|
||||||
($r:expr) => (
|
($r:expr) => (
|
||||||
Line::BuiltIn(BuiltInInstruction::SetCutPoint($r))
|
Line::PolicyExempt(PEInstruction::SetCutPoint($r))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user