Support %_IsObject in Crankshaft.
Review URL: http://codereview.chromium.org/5806001 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@6027 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
eb7b4e347e
commit
2996dca6aa
@ -206,6 +206,13 @@ void LIsNullAndBranch::PrintDataTo(StringStream* stream) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void LIsObjectAndBranch::PrintDataTo(StringStream* stream) const {
|
||||||
|
stream->Add("if is_object(");
|
||||||
|
input()->PrintTo(stream);
|
||||||
|
stream->Add(") then B%d else B%d", true_block_id(), false_block_id());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void LIsSmiAndBranch::PrintDataTo(StringStream* stream) const {
|
void LIsSmiAndBranch::PrintDataTo(StringStream* stream) const {
|
||||||
stream->Add("if is_smi(");
|
stream->Add("if is_smi(");
|
||||||
input()->PrintTo(stream);
|
input()->PrintTo(stream);
|
||||||
@ -1238,6 +1245,17 @@ LInstruction* LChunkBuilder::DoBranch(HBranch* instr) {
|
|||||||
temp,
|
temp,
|
||||||
first_id,
|
first_id,
|
||||||
second_id);
|
second_id);
|
||||||
|
} else if (v->IsIsObject()) {
|
||||||
|
HIsObject* compare = HIsObject::cast(v);
|
||||||
|
ASSERT(compare->value()->representation().IsTagged());
|
||||||
|
|
||||||
|
LOperand* temp1 = TempRegister();
|
||||||
|
LOperand* temp2 = TempRegister();
|
||||||
|
return new LIsObjectAndBranch(UseRegisterAtStart(compare->value()),
|
||||||
|
temp1,
|
||||||
|
temp2,
|
||||||
|
first_id,
|
||||||
|
second_id);
|
||||||
} else if (v->IsCompareJSObjectEq()) {
|
} else if (v->IsCompareJSObjectEq()) {
|
||||||
HCompareJSObjectEq* compare = HCompareJSObjectEq::cast(v);
|
HCompareJSObjectEq* compare = HCompareJSObjectEq::cast(v);
|
||||||
return new LCmpJSObjectEqAndBranch(UseRegisterAtStart(compare->left()),
|
return new LCmpJSObjectEqAndBranch(UseRegisterAtStart(compare->left()),
|
||||||
@ -1600,6 +1618,14 @@ LInstruction* LChunkBuilder::DoIsNull(HIsNull* instr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LInstruction* LChunkBuilder::DoIsObject(HIsObject* instr) {
|
||||||
|
ASSERT(instr->value()->representation().IsTagged());
|
||||||
|
LOperand* value = UseRegisterAtStart(instr->value());
|
||||||
|
|
||||||
|
return DefineAsRegister(new LIsObject(value, TempRegister()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
LInstruction* LChunkBuilder::DoIsSmi(HIsSmi* instr) {
|
LInstruction* LChunkBuilder::DoIsSmi(HIsSmi* instr) {
|
||||||
ASSERT(instr->value()->representation().IsTagged());
|
ASSERT(instr->value()->representation().IsTagged());
|
||||||
LOperand* value = UseAtStart(instr->value());
|
LOperand* value = UseAtStart(instr->value());
|
||||||
|
@ -121,6 +121,8 @@ class Translation;
|
|||||||
// LInteger32ToDouble
|
// LInteger32ToDouble
|
||||||
// LIsNull
|
// LIsNull
|
||||||
// LIsNullAndBranch
|
// LIsNullAndBranch
|
||||||
|
// LIsObject
|
||||||
|
// LIsObjectAndBranch
|
||||||
// LIsSmi
|
// LIsSmi
|
||||||
// LIsSmiAndBranch
|
// LIsSmiAndBranch
|
||||||
// LLoadNamedField
|
// LLoadNamedField
|
||||||
@ -203,6 +205,8 @@ class Translation;
|
|||||||
V(Integer32ToDouble) \
|
V(Integer32ToDouble) \
|
||||||
V(IsNull) \
|
V(IsNull) \
|
||||||
V(IsNullAndBranch) \
|
V(IsNullAndBranch) \
|
||||||
|
V(IsObject) \
|
||||||
|
V(IsObjectAndBranch) \
|
||||||
V(IsSmi) \
|
V(IsSmi) \
|
||||||
V(IsSmiAndBranch) \
|
V(IsSmiAndBranch) \
|
||||||
V(HasInstanceType) \
|
V(HasInstanceType) \
|
||||||
@ -742,6 +746,48 @@ class LIsNullAndBranch: public LIsNull {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class LIsObject: public LUnaryOperation {
|
||||||
|
public:
|
||||||
|
LIsObject(LOperand* value, LOperand* temp)
|
||||||
|
: LUnaryOperation(value), temp_(temp) {}
|
||||||
|
|
||||||
|
DECLARE_CONCRETE_INSTRUCTION(IsObject, "is-object")
|
||||||
|
|
||||||
|
LOperand* temp() const { return temp_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
LOperand* temp_;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class LIsObjectAndBranch: public LIsObject {
|
||||||
|
public:
|
||||||
|
LIsObjectAndBranch(LOperand* value,
|
||||||
|
LOperand* temp,
|
||||||
|
LOperand* temp2,
|
||||||
|
int true_block_id,
|
||||||
|
int false_block_id)
|
||||||
|
: LIsObject(value, temp),
|
||||||
|
temp2_(temp2),
|
||||||
|
true_block_id_(true_block_id),
|
||||||
|
false_block_id_(false_block_id) { }
|
||||||
|
|
||||||
|
DECLARE_CONCRETE_INSTRUCTION(IsObjectAndBranch, "is-object-and-branch")
|
||||||
|
virtual void PrintDataTo(StringStream* stream) const;
|
||||||
|
virtual bool IsControl() const { return true; }
|
||||||
|
|
||||||
|
int true_block_id() const { return true_block_id_; }
|
||||||
|
int false_block_id() const { return false_block_id_; }
|
||||||
|
|
||||||
|
LOperand* temp2() const { return temp2_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
LOperand* temp2_;
|
||||||
|
int true_block_id_;
|
||||||
|
int false_block_id_;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
class LIsSmi: public LUnaryOperation {
|
class LIsSmi: public LUnaryOperation {
|
||||||
public:
|
public:
|
||||||
explicit LIsSmi(LOperand* value) : LUnaryOperation(value) {}
|
explicit LIsSmi(LOperand* value) : LUnaryOperation(value) {}
|
||||||
|
@ -1213,6 +1213,26 @@ void LCodeGen::DoIsNullAndBranch(LIsNullAndBranch* instr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Condition LCodeGen::EmitIsObject(Register input,
|
||||||
|
Register temp1,
|
||||||
|
Register temp2,
|
||||||
|
Label* is_not_object,
|
||||||
|
Label* is_object) {
|
||||||
|
Abort("EmitIsObject unimplemented.");
|
||||||
|
return ne;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void LCodeGen::DoIsObject(LIsObject* instr) {
|
||||||
|
Abort("DoIsObject unimplemented.");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void LCodeGen::DoIsObjectAndBranch(LIsObjectAndBranch* instr) {
|
||||||
|
Abort("DoIsObjectAndBranch unimplemented.");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void LCodeGen::DoIsSmi(LIsSmi* instr) {
|
void LCodeGen::DoIsSmi(LIsSmi* instr) {
|
||||||
ASSERT(instr->hydrogen()->value()->representation().IsTagged());
|
ASSERT(instr->hydrogen()->value()->representation().IsTagged());
|
||||||
Register result = ToRegister(instr->result());
|
Register result = ToRegister(instr->result());
|
||||||
|
@ -208,6 +208,15 @@ class LCodeGen BASE_EMBEDDED {
|
|||||||
Condition EmitTypeofIs(Label* true_label, Label* false_label,
|
Condition EmitTypeofIs(Label* true_label, Label* false_label,
|
||||||
Register input, Handle<String> type_name);
|
Register input, Handle<String> type_name);
|
||||||
|
|
||||||
|
// Emits optimized code for %_IsObject(x). Preserves input register.
|
||||||
|
// Returns the condition on which a final split to
|
||||||
|
// true and false label should be made, to optimize fallthrough.
|
||||||
|
Condition EmitIsObject(Register input,
|
||||||
|
Register temp1,
|
||||||
|
Register temp2,
|
||||||
|
Label* is_not_object,
|
||||||
|
Label* is_object);
|
||||||
|
|
||||||
LChunk* const chunk_;
|
LChunk* const chunk_;
|
||||||
MacroAssembler* const masm_;
|
MacroAssembler* const masm_;
|
||||||
CompilationInfo* const info_;
|
CompilationInfo* const info_;
|
||||||
|
@ -140,6 +140,7 @@ class LChunkBuilder;
|
|||||||
// HHasCachedArrayIndex
|
// HHasCachedArrayIndex
|
||||||
// HHasInstanceType
|
// HHasInstanceType
|
||||||
// HIsNull
|
// HIsNull
|
||||||
|
// HIsObject
|
||||||
// HIsSmi
|
// HIsSmi
|
||||||
// HValueOf
|
// HValueOf
|
||||||
// HUnknownOSRValue
|
// HUnknownOSRValue
|
||||||
@ -208,6 +209,7 @@ class LChunkBuilder;
|
|||||||
V(Goto) \
|
V(Goto) \
|
||||||
V(InstanceOf) \
|
V(InstanceOf) \
|
||||||
V(IsNull) \
|
V(IsNull) \
|
||||||
|
V(IsObject) \
|
||||||
V(IsSmi) \
|
V(IsSmi) \
|
||||||
V(HasInstanceType) \
|
V(HasInstanceType) \
|
||||||
V(HasCachedArrayIndex) \
|
V(HasCachedArrayIndex) \
|
||||||
@ -2100,6 +2102,14 @@ class HIsNull: public HUnaryPredicate {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class HIsObject: public HUnaryPredicate {
|
||||||
|
public:
|
||||||
|
explicit HIsObject(HValue* value) : HUnaryPredicate(value) { }
|
||||||
|
|
||||||
|
DECLARE_CONCRETE_INSTRUCTION(IsObject, "is_object")
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
class HIsSmi: public HUnaryPredicate {
|
class HIsSmi: public HUnaryPredicate {
|
||||||
public:
|
public:
|
||||||
explicit HIsSmi(HValue* value) : HUnaryPredicate(value) { }
|
explicit HIsSmi(HValue* value) : HUnaryPredicate(value) { }
|
||||||
|
@ -4950,14 +4950,18 @@ void HGraphBuilder::GenerateIsRegExp(int argument_count, int ast_id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void HGraphBuilder::GenerateIsNonNegativeSmi(int argument_count,
|
void HGraphBuilder::GenerateIsObject(int argument_count, int ast_id) {
|
||||||
int ast_id) {
|
ASSERT(argument_count == 1);
|
||||||
BAILOUT("inlined runtime function: IsNonNegativeSmi");
|
|
||||||
|
HValue* value = Pop();
|
||||||
|
HIsObject* test = new HIsObject(value);
|
||||||
|
ast_context()->ReturnInstruction(test, ast_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void HGraphBuilder::GenerateIsObject(int argument_count, int ast_id) {
|
void HGraphBuilder::GenerateIsNonNegativeSmi(int argument_count,
|
||||||
BAILOUT("inlined runtime function: IsObject");
|
int ast_id) {
|
||||||
|
BAILOUT("inlined runtime function: IsNonNegativeSmi");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1403,6 +1403,71 @@ void LCodeGen::DoIsNullAndBranch(LIsNullAndBranch* instr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Condition LCodeGen::EmitIsObject(Register input,
|
||||||
|
Register temp1,
|
||||||
|
Register temp2,
|
||||||
|
Label* is_not_object,
|
||||||
|
Label* is_object) {
|
||||||
|
ASSERT(!input.is(temp1));
|
||||||
|
ASSERT(!input.is(temp2));
|
||||||
|
ASSERT(!temp1.is(temp2));
|
||||||
|
|
||||||
|
__ test(input, Immediate(kSmiTagMask));
|
||||||
|
__ j(equal, is_not_object);
|
||||||
|
|
||||||
|
__ cmp(input, Factory::null_value());
|
||||||
|
__ j(equal, is_object);
|
||||||
|
|
||||||
|
__ mov(temp1, FieldOperand(input, HeapObject::kMapOffset));
|
||||||
|
// Undetectable objects behave like undefined.
|
||||||
|
__ movzx_b(temp2, FieldOperand(temp1, Map::kBitFieldOffset));
|
||||||
|
__ test(temp2, Immediate(1 << Map::kIsUndetectable));
|
||||||
|
__ j(not_zero, is_not_object);
|
||||||
|
|
||||||
|
__ movzx_b(temp2, FieldOperand(temp1, Map::kInstanceTypeOffset));
|
||||||
|
__ cmp(temp2, FIRST_JS_OBJECT_TYPE);
|
||||||
|
__ j(below, is_not_object);
|
||||||
|
__ cmp(temp2, LAST_JS_OBJECT_TYPE);
|
||||||
|
return below_equal;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void LCodeGen::DoIsObject(LIsObject* instr) {
|
||||||
|
Register reg = ToRegister(instr->input());
|
||||||
|
Register result = ToRegister(instr->result());
|
||||||
|
Register temp = ToRegister(instr->temp());
|
||||||
|
Label is_false, is_true, done;
|
||||||
|
|
||||||
|
Condition true_cond = EmitIsObject(reg, result, temp, &is_false, &is_true);
|
||||||
|
__ j(true_cond, &is_true);
|
||||||
|
|
||||||
|
__ bind(&is_false);
|
||||||
|
__ mov(result, Handle<Object>(Heap::false_value()));
|
||||||
|
__ jmp(&done);
|
||||||
|
|
||||||
|
__ bind(&is_true);
|
||||||
|
__ mov(result, Handle<Object>(Heap::true_value()));
|
||||||
|
|
||||||
|
__ bind(&done);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void LCodeGen::DoIsObjectAndBranch(LIsObjectAndBranch* instr) {
|
||||||
|
Register reg = ToRegister(instr->input());
|
||||||
|
Register temp = ToRegister(instr->temp());
|
||||||
|
Register temp2 = ToRegister(instr->temp2());
|
||||||
|
|
||||||
|
int true_block = chunk_->LookupDestination(instr->true_block_id());
|
||||||
|
int false_block = chunk_->LookupDestination(instr->false_block_id());
|
||||||
|
Label* true_label = chunk_->GetAssemblyLabel(true_block);
|
||||||
|
Label* false_label = chunk_->GetAssemblyLabel(false_block);
|
||||||
|
|
||||||
|
Condition true_cond = EmitIsObject(reg, temp, temp2, false_label, true_label);
|
||||||
|
|
||||||
|
EmitBranch(true_block, false_block, true_cond);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void LCodeGen::DoIsSmi(LIsSmi* instr) {
|
void LCodeGen::DoIsSmi(LIsSmi* instr) {
|
||||||
Operand input = ToOperand(instr->input());
|
Operand input = ToOperand(instr->input());
|
||||||
Register result = ToRegister(instr->result());
|
Register result = ToRegister(instr->result());
|
||||||
|
@ -197,6 +197,15 @@ class LCodeGen BASE_EMBEDDED {
|
|||||||
Condition EmitTypeofIs(Label* true_label, Label* false_label,
|
Condition EmitTypeofIs(Label* true_label, Label* false_label,
|
||||||
Register input, Handle<String> type_name);
|
Register input, Handle<String> type_name);
|
||||||
|
|
||||||
|
// Emits optimized code for %_IsObject(x). Preserves input register.
|
||||||
|
// Returns the condition on which a final split to
|
||||||
|
// true and false label should be made, to optimize fallthrough.
|
||||||
|
Condition EmitIsObject(Register input,
|
||||||
|
Register temp1,
|
||||||
|
Register temp2,
|
||||||
|
Label* is_not_object,
|
||||||
|
Label* is_object);
|
||||||
|
|
||||||
LChunk* const chunk_;
|
LChunk* const chunk_;
|
||||||
MacroAssembler* const masm_;
|
MacroAssembler* const masm_;
|
||||||
CompilationInfo* const info_;
|
CompilationInfo* const info_;
|
||||||
|
@ -206,6 +206,13 @@ void LIsNullAndBranch::PrintDataTo(StringStream* stream) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void LIsObjectAndBranch::PrintDataTo(StringStream* stream) const {
|
||||||
|
stream->Add("if is_object(");
|
||||||
|
input()->PrintTo(stream);
|
||||||
|
stream->Add(") then B%d else B%d", true_block_id(), false_block_id());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void LIsSmiAndBranch::PrintDataTo(StringStream* stream) const {
|
void LIsSmiAndBranch::PrintDataTo(StringStream* stream) const {
|
||||||
stream->Add("if is_smi(");
|
stream->Add("if is_smi(");
|
||||||
input()->PrintTo(stream);
|
input()->PrintTo(stream);
|
||||||
@ -1251,6 +1258,17 @@ LInstruction* LChunkBuilder::DoBranch(HBranch* instr) {
|
|||||||
temp,
|
temp,
|
||||||
first_id,
|
first_id,
|
||||||
second_id);
|
second_id);
|
||||||
|
} else if (v->IsIsObject()) {
|
||||||
|
HIsObject* compare = HIsObject::cast(v);
|
||||||
|
ASSERT(compare->value()->representation().IsTagged());
|
||||||
|
|
||||||
|
LOperand* temp1 = TempRegister();
|
||||||
|
LOperand* temp2 = TempRegister();
|
||||||
|
return new LIsObjectAndBranch(UseRegisterAtStart(compare->value()),
|
||||||
|
temp1,
|
||||||
|
temp2,
|
||||||
|
first_id,
|
||||||
|
second_id);
|
||||||
} else if (v->IsCompareJSObjectEq()) {
|
} else if (v->IsCompareJSObjectEq()) {
|
||||||
HCompareJSObjectEq* compare = HCompareJSObjectEq::cast(v);
|
HCompareJSObjectEq* compare = HCompareJSObjectEq::cast(v);
|
||||||
return new LCmpJSObjectEqAndBranch(UseRegisterAtStart(compare->left()),
|
return new LCmpJSObjectEqAndBranch(UseRegisterAtStart(compare->left()),
|
||||||
@ -1630,6 +1648,14 @@ LInstruction* LChunkBuilder::DoIsNull(HIsNull* instr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LInstruction* LChunkBuilder::DoIsObject(HIsObject* instr) {
|
||||||
|
ASSERT(instr->value()->representation().IsTagged());
|
||||||
|
LOperand* value = UseRegisterAtStart(instr->value());
|
||||||
|
|
||||||
|
return DefineAsRegister(new LIsObject(value, TempRegister()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
LInstruction* LChunkBuilder::DoIsSmi(HIsSmi* instr) {
|
LInstruction* LChunkBuilder::DoIsSmi(HIsSmi* instr) {
|
||||||
ASSERT(instr->value()->representation().IsTagged());
|
ASSERT(instr->value()->representation().IsTagged());
|
||||||
LOperand* value = UseAtStart(instr->value());
|
LOperand* value = UseAtStart(instr->value());
|
||||||
|
@ -124,6 +124,8 @@ class LGapNode;
|
|||||||
// LInteger32ToDouble
|
// LInteger32ToDouble
|
||||||
// LIsNull
|
// LIsNull
|
||||||
// LIsNullAndBranch
|
// LIsNullAndBranch
|
||||||
|
// LIsObject
|
||||||
|
// LIsObjectAndBranch
|
||||||
// LIsSmi
|
// LIsSmi
|
||||||
// LIsSmiAndBranch
|
// LIsSmiAndBranch
|
||||||
// LLoadNamedField
|
// LLoadNamedField
|
||||||
@ -206,6 +208,8 @@ class LGapNode;
|
|||||||
V(Integer32ToDouble) \
|
V(Integer32ToDouble) \
|
||||||
V(IsNull) \
|
V(IsNull) \
|
||||||
V(IsNullAndBranch) \
|
V(IsNullAndBranch) \
|
||||||
|
V(IsObject) \
|
||||||
|
V(IsObjectAndBranch) \
|
||||||
V(IsSmi) \
|
V(IsSmi) \
|
||||||
V(IsSmiAndBranch) \
|
V(IsSmiAndBranch) \
|
||||||
V(HasInstanceType) \
|
V(HasInstanceType) \
|
||||||
@ -747,6 +751,48 @@ class LIsNullAndBranch: public LIsNull {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class LIsObject: public LUnaryOperation {
|
||||||
|
public:
|
||||||
|
LIsObject(LOperand* value, LOperand* temp)
|
||||||
|
: LUnaryOperation(value), temp_(temp) {}
|
||||||
|
|
||||||
|
DECLARE_CONCRETE_INSTRUCTION(IsObject, "is-object")
|
||||||
|
|
||||||
|
LOperand* temp() const { return temp_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
LOperand* temp_;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class LIsObjectAndBranch: public LIsObject {
|
||||||
|
public:
|
||||||
|
LIsObjectAndBranch(LOperand* value,
|
||||||
|
LOperand* temp,
|
||||||
|
LOperand* temp2,
|
||||||
|
int true_block_id,
|
||||||
|
int false_block_id)
|
||||||
|
: LIsObject(value, temp),
|
||||||
|
temp2_(temp2),
|
||||||
|
true_block_id_(true_block_id),
|
||||||
|
false_block_id_(false_block_id) { }
|
||||||
|
|
||||||
|
DECLARE_CONCRETE_INSTRUCTION(IsObjectAndBranch, "is-object-and-branch")
|
||||||
|
virtual void PrintDataTo(StringStream* stream) const;
|
||||||
|
virtual bool IsControl() const { return true; }
|
||||||
|
|
||||||
|
int true_block_id() const { return true_block_id_; }
|
||||||
|
int false_block_id() const { return false_block_id_; }
|
||||||
|
|
||||||
|
LOperand* temp2() const { return temp2_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
LOperand* temp2_;
|
||||||
|
int true_block_id_;
|
||||||
|
int false_block_id_;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
class LIsSmi: public LUnaryOperation {
|
class LIsSmi: public LUnaryOperation {
|
||||||
public:
|
public:
|
||||||
explicit LIsSmi(LOperand* value) : LUnaryOperation(value) {}
|
explicit LIsSmi(LOperand* value) : LUnaryOperation(value) {}
|
||||||
|
Loading…
Reference in New Issue
Block a user