bool ExpEvaluator::runOperation(ObjList& stack, const ExpOperation& oper, GenObject* context) const { DDebug(this,DebugAll,"runOperation(%p,%u,%p) %s",&stack,oper.opcode(),context,getOperator(oper.opcode())); XDebug(this,DebugAll,"stack: %s",dump(stack).c_str()); bool boolRes = true; switch (oper.opcode()) { case OpcPush: case OpcField: pushOne(stack,oper.clone()); break; case OpcCopy: { Mutex* mtx = 0; ScriptRun* runner = YOBJECT(ScriptRun,&oper); if (runner) { if (runner->context()) mtx = runner->context()->mutex(); if (!mtx) mtx = runner; } pushOne(stack,oper.copy(mtx)); } break; case OpcNone: case OpcLabel: break; case OpcDrop: TelEngine::destruct(popOne(stack)); break; case OpcDup: { ExpOperation* op = popValue(stack,context); if (!op) return gotError("ExpEvaluator stack underflow",oper.lineNumber()); pushOne(stack,op->clone()); pushOne(stack,op); } break; case OpcAnd: case OpcOr: case OpcXor: case OpcShl: case OpcShr: case OpcAdd: case OpcSub: case OpcMul: case OpcDiv: case OpcMod: boolRes = false; // fall through case OpcEq: case OpcNe: case OpcLt: case OpcGt: case OpcLe: case OpcGe: { ExpOperation* op2 = popValue(stack,context); ExpOperation* op1 = popValue(stack,context); if (!op1 || !op2) { TelEngine::destruct(op1); TelEngine::destruct(op2); return gotError("ExpEvaluator stack underflow",oper.lineNumber()); } switch (oper.opcode()) { case OpcDiv: case OpcMod: if (!op2->valInteger()) return gotError("Division by zero",oper.lineNumber()); case OpcAdd: if (op1->isInteger() && op2->isInteger()) break; // turn addition into concatenation { String val = *op1 + *op2; TelEngine::destruct(op1); TelEngine::destruct(op2); DDebug(this,DebugAll,"String result: '%s'",val.c_str()); pushOne(stack,new ExpOperation(val)); return true; } default: break; } long int val = 0; switch (oper.opcode()) { case OpcAnd: val = op1->valInteger() & op2->valInteger(); break; case OpcOr: val = op1->valInteger() | op2->valInteger(); break; case OpcXor: val = op1->valInteger() ^ op2->valInteger(); break; case OpcShl: val = op1->valInteger() << op2->valInteger(); break; case OpcShr: val = op1->valInteger() >> op2->valInteger(); break; case OpcAdd: val = op1->valInteger() + op2->valInteger(); break; case OpcSub: val = op1->valInteger() - op2->valInteger(); break; case OpcMul: val = op1->valInteger() * op2->valInteger(); break; case OpcDiv: val = op1->valInteger() / op2->valInteger(); break; case OpcMod: val = op1->valInteger() % op2->valInteger(); break; case OpcLt: val = (op1->valInteger() < op2->valInteger()) ? 1 : 0; break; case OpcGt: val = (op1->valInteger() > op2->valInteger()) ? 1 : 0; break; case OpcLe: val = (op1->valInteger() <= op2->valInteger()) ? 1 : 0; break; case OpcGe: val = (op1->valInteger() >= op2->valInteger()) ? 1 : 0; break; case OpcEq: val = (*op1 == *op2) ? 1 : 0; break; case OpcNe: val = (*op1 != *op2) ? 1 : 0; break; default: break; } TelEngine::destruct(op1); TelEngine::destruct(op2); if (boolRes) { DDebug(this,DebugAll,"Bool result: '%s'",String::boolText(val != 0)); pushOne(stack,new ExpOperation(val != 0)); } else { DDebug(this,DebugAll,"Numeric result: %lu",val); pushOne(stack,new ExpOperation(val)); } } break; case OpcLAnd: case OpcLOr: { ExpOperation* op2 = popValue(stack,context); ExpOperation* op1 = popValue(stack,context); if (!op1 || !op2) { TelEngine::destruct(op1); TelEngine::destruct(op2); return gotError("ExpEvaluator stack underflow",oper.lineNumber()); } bool val = false; switch (oper.opcode()) { case OpcLAnd: val = op1->valBoolean() && op2->valBoolean(); break; case OpcLOr: val = op1->valBoolean() || op2->valBoolean(); break; default: break; } TelEngine::destruct(op1); TelEngine::destruct(op2); DDebug(this,DebugAll,"Bool result: '%s'",String::boolText(val)); pushOne(stack,new ExpOperation(val)); } break; case OpcCat: { ExpOperation* op2 = popValue(stack,context); ExpOperation* op1 = popValue(stack,context); if (!op1 || !op2) { TelEngine::destruct(op1); TelEngine::destruct(op2); return gotError("ExpEvaluator stack underflow",oper.lineNumber()); } String val = *op1 + *op2; TelEngine::destruct(op1); TelEngine::destruct(op2); DDebug(this,DebugAll,"String result: '%s'",val.c_str()); pushOne(stack,new ExpOperation(val)); } break; case OpcAs: { ExpOperation* op2 = popOne(stack); ExpOperation* op1 = popOne(stack); if (!op1 || !op2) { TelEngine::destruct(op1); TelEngine::destruct(op2); return gotError("ExpEvaluator stack underflow",oper.lineNumber()); } pushOne(stack,op1->clone(*op2)); TelEngine::destruct(op1); TelEngine::destruct(op2); } break; case OpcNeg: case OpcNot: case OpcLNot: { ExpOperation* op = popValue(stack,context); if (!op) return gotError("ExpEvaluator stack underflow",oper.lineNumber()); switch (oper.opcode()) { case OpcNeg: pushOne(stack,new ExpOperation(-op->valInteger())); break; case OpcNot: pushOne(stack,new ExpOperation(~op->valInteger())); break; case OpcLNot: pushOne(stack,new ExpOperation(!op->valBoolean())); break; default: pushOne(stack,new ExpOperation(op->valInteger())); break; } TelEngine::destruct(op); } break; case OpcFunc: return runFunction(stack,oper,context) || gotError("Function '" + oper.name() + "' call failed",oper.lineNumber()); case OpcIncPre: case OpcDecPre: case OpcIncPost: case OpcDecPost: { ExpOperation* fld = popOne(stack); if (!fld) return gotError("ExpEvaluator stack underflow",oper.lineNumber()); if (fld->opcode() != OpcField) { TelEngine::destruct(fld); return gotError("Expecting LValue in operator",oper.lineNumber()); } ExpOperation* val = 0; if (!(runField(stack,*fld,context) && (val = popOne(stack)))) { TelEngine::destruct(fld); return false; } long int num = val->valInteger(); switch (oper.opcode()) { case OpcIncPre: num++; (*val) = num; break; case OpcDecPre: num--; (*val) = num; break; case OpcIncPost: (*val) = num; num++; break; case OpcDecPost: (*val) = num; num--; break; default: break; } (*fld) = num; bool ok = runAssign(stack,*fld,context); TelEngine::destruct(fld); if (!ok) { TelEngine::destruct(val); return gotError("Assignment failed",oper.lineNumber()); } pushOne(stack,val); } break; case OpcAssign: { ExpOperation* val = popValue(stack,context); ExpOperation* fld = popOne(stack); if (!fld || !val) { TelEngine::destruct(fld); TelEngine::destruct(val); return gotError("ExpEvaluator stack underflow",oper.lineNumber()); } if (fld->opcode() != OpcField) { TelEngine::destruct(fld); TelEngine::destruct(val); return gotError("Expecting LValue in assignment",oper.lineNumber()); } ExpOperation* op = val->clone(fld->name()); TelEngine::destruct(fld); bool ok = runAssign(stack,*op,context); TelEngine::destruct(op); if (!ok) { TelEngine::destruct(val); return gotError("Assignment failed",oper.lineNumber()); } pushOne(stack,val); } break; default: if (oper.opcode() & OpcAssign) { // assignment by operation ExpOperation* val = popValue(stack,context); ExpOperation* fld = popOne(stack); if (!fld || !val) { TelEngine::destruct(fld); TelEngine::destruct(val); return gotError("ExpEvaluator stack underflow",oper.lineNumber()); } if (fld->opcode() != OpcField) { TelEngine::destruct(fld); TelEngine::destruct(val); return gotError("Expecting LValue in assignment",oper.lineNumber()); } pushOne(stack,fld->clone()); pushOne(stack,fld); pushOne(stack,val); ExpOperation op((Opcode)(oper.opcode() & ~OpcAssign), oper.name(),oper.number(),oper.barrier()); if (!runOperation(stack,op,context)) return false; static const ExpOperation assign(OpcAssign); return runOperation(stack,assign,context); } Debug(this,DebugStub,"Please implement operation %u '%s'", oper.opcode(),getOperator(oper.opcode())); return false; } return true; }
bool ExpEvaluator::trySimplify() { DDebug(this,DebugInfo,"trySimplify"); bool done = false; for (unsigned int i = 0; ; i++) { ExpOperation* o = static_cast<ExpOperation*>(m_opcodes[i]); if (!o) { if (i >= m_opcodes.length()) break; else continue; } if (o->barrier()) continue; switch (o->opcode()) { case OpcLAnd: case OpcLOr: case OpcLXor: case OpcAnd: case OpcOr: case OpcXor: case OpcShl: case OpcShr: case OpcAdd: case OpcSub: case OpcMul: case OpcDiv: case OpcMod: case OpcCat: case OpcEq: case OpcNe: case OpcLt: case OpcGt: case OpcLe: case OpcGe: if (i >= 2) { ExpOperation* op2 = static_cast<ExpOperation*>(m_opcodes[i-1]); ExpOperation* op1 = static_cast<ExpOperation*>(m_opcodes[i-2]); if (!op1 || !op2) continue; if (o->opcode() == OpcLAnd || o->opcode() == OpcAnd || o->opcode() == OpcMul) { if ((op1->opcode() == OpcPush && !op1->number() && op2->opcode() == OpcField) || (op2->opcode() == OpcPush && !op2->number() && op1->opcode() == OpcField)) { ExpOperation* newOp = (o->opcode() == OpcLAnd) ? new ExpOperation(false) : new ExpOperation((long int)0); newOp->lineNumber(o->lineNumber()); (m_opcodes+i)->set(newOp); m_opcodes.remove(op1); m_opcodes.remove(op2); i -= 2; done = true; continue; } } if (o->opcode() == OpcLOr) { if ((op1->opcode() == OpcPush && op1->number() && op2->opcode() == OpcField) || (op2->opcode() == OpcPush && op2->number() && op1->opcode() == OpcField)) { ExpOperation* newOp = new ExpOperation(true); newOp->lineNumber(o->lineNumber()); (m_opcodes+i)->set(newOp); m_opcodes.remove(op1); m_opcodes.remove(op2); i -= 2; done = true; continue; } } if ((op1->opcode() == OpcPush) && (op2->opcode() == OpcPush)) { ObjList stack; pushOne(stack,op1->clone()); pushOne(stack,op2->clone()); if (runOperation(stack,*o)) { // replace operators and operation with computed constant ExpOperation* newOp = popOne(stack); newOp->lineNumber(o->lineNumber()); (m_opcodes+i)->set(newOp); m_opcodes.remove(op1); m_opcodes.remove(op2); i -= 2; done = true; } } } break; case OpcNeg: case OpcNot: case OpcLNot: if (i >= 1) { ExpOperation* op = static_cast<ExpOperation*>(m_opcodes[i-1]); if (!op) continue; if (op->opcode() == OpcPush) { ObjList stack; pushOne(stack,op->clone()); if (runOperation(stack,*o)) { // replace unary operator and operation with computed constant ExpOperation* newOp = popOne(stack); newOp->lineNumber(o->lineNumber()); (m_opcodes+i)->set(newOp); m_opcodes.remove(op); i--; done = true; } } else if (op->opcode() == o->opcode() && op->opcode() != OpcLNot) { // minus or bit negation applied twice - remove both operators m_opcodes.remove(o); m_opcodes.remove(op); i--; done = true; } } break; default: break; } } return done; }