void Expression::simpleArithmetic1(StackFrame &stackFrame) { switch (stackFrame.opers[-1]) { case OP_ADD: if (stackFrame.opers[-2] == OP_LOAD_IMM_STR) { if ((char *)decodePtr(stackFrame.values[-2]) != _resultStr) { strcpy(_resultStr, (char *)decodePtr(stackFrame.values[-2])); stackFrame.values[-2] = encodePtr((byte *)_resultStr, kResStr); } strcat(_resultStr, (char *)decodePtr(stackFrame.values[0])); stackFrame.pop(2); } break; case OP_MUL: stackFrame.values[-2] *= stackFrame.values[0]; stackFrame.pop(2); break; case OP_DIV: stackFrame.values[-2] /= stackFrame.values[0]; stackFrame.pop(2); break; case OP_MOD: stackFrame.values[-2] %= stackFrame.values[0]; stackFrame.pop(2); break; case OP_BITAND: stackFrame.values[-2] &= stackFrame.values[0]; stackFrame.pop(2); break; } }
int Expression::cmpHelper(const StackFrame &stackFrame) { byte type = stackFrame.opers[-3]; int cmpTemp = 0; if (type == OP_LOAD_IMM_INT16) { cmpTemp = (int)stackFrame.values[-3] - (int)stackFrame.values[-1]; } else if (type == OP_LOAD_IMM_STR) { if ((char *)decodePtr(stackFrame.values[-3]) != _resultStr) { strcpy(_resultStr, (char *)decodePtr(stackFrame.values[-3])); stackFrame.values[-3] = encodePtr((byte *)_resultStr, kResStr); } cmpTemp = strcmp(_resultStr, (char *)decodePtr(stackFrame.values[-1])); } return cmpTemp; }
/* SOAP client calls this function to parse response from SOAP server */ bool parse_packet_soap(SoapClient *obj, const char *buffer, int buffer_size, std::shared_ptr<sdlFunction> fn, const char *fn_name, Variant &return_value, Array& soap_headers) { char* envelope_ns = nullptr; xmlNodePtr trav, env, head, body, resp, cur, fault; xmlAttrPtr attr; int param_count = 0; int soap_version = SOAP_1_1; sdlSoapBindingFunctionHeaderMap *hdrs = nullptr; assert(return_value.asTypedValue()->m_type == KindOfUninit); return_value.asTypedValue()->m_type = KindOfNull; /* Response for one-way opearation */ if (buffer_size == 0) { return true; } /* Parse XML packet */ xmlDocPtr response = soap_xmlParseMemory(buffer, buffer_size); if (!response) { add_soap_fault(obj, "Client", "looks like we got no XML document"); return false; } if (xmlGetIntSubset(response) != nullptr) { add_soap_fault(obj, "Client", "DTD are not supported by SOAP"); xmlFreeDoc(response); return false; } /* Get <Envelope> element */ env = nullptr; trav = response->children; while (trav != nullptr) { if (trav->type == XML_ELEMENT_NODE) { if (!env && node_is_equal_ex(trav,"Envelope", SOAP_1_1_ENV_NAMESPACE)) { env = trav; envelope_ns = SOAP_1_1_ENV_NAMESPACE; soap_version = SOAP_1_1; } else if (!env && node_is_equal_ex(trav, "Envelope", SOAP_1_2_ENV_NAMESPACE)) { env = trav; envelope_ns = SOAP_1_2_ENV_NAMESPACE; soap_version = SOAP_1_2; } else { add_soap_fault(obj, "VersionMismatch", "Wrong Version"); xmlFreeDoc(response); return false; } } trav = trav->next; } if (env == nullptr) { add_soap_fault(obj, "Client", "looks like we got XML without \"Envelope\" element"); xmlFreeDoc(response); return false; } attr = env->properties; while (attr != nullptr) { if (attr->ns == nullptr) { add_soap_fault(obj, "Client", "A SOAP Envelope element cannot have non Namespace " "qualified attributes"); xmlFreeDoc(response); return false; } if (attr_is_equal_ex(attr, "encodingStyle", SOAP_1_2_ENV_NAMESPACE)) { if (soap_version == SOAP_1_2) { add_soap_fault(obj, "Client", "encodingStyle cannot be specified on the Envelope"); xmlFreeDoc(response); return false; } if (strcmp((char*)attr->children->content, SOAP_1_1_ENC_NAMESPACE)) { add_soap_fault(obj, "Client", "Unknown data encoding style"); xmlFreeDoc(response); return false; } } attr = attr->next; } /* Get <Header> element */ head = nullptr; trav = env->children; while (trav != nullptr && trav->type != XML_ELEMENT_NODE) { trav = trav->next; } if (trav != nullptr && node_is_equal_ex(trav,"Header",envelope_ns)) { head = trav; trav = trav->next; } /* Get <Body> element */ body = nullptr; while (trav != nullptr && trav->type != XML_ELEMENT_NODE) { trav = trav->next; } if (trav != nullptr && node_is_equal_ex(trav,"Body",envelope_ns)) { body = trav; trav = trav->next; } while (trav != nullptr && trav->type != XML_ELEMENT_NODE) { trav = trav->next; } if (body == nullptr) { add_soap_fault(obj, "Client", "Body must be present in a SOAP envelope"); xmlFreeDoc(response); return false; } attr = body->properties; while (attr != nullptr) { if (attr->ns == nullptr) { if (soap_version == SOAP_1_2) { add_soap_fault(obj, "Client", "A SOAP Body element cannot have non Namespace " "qualified attributes"); xmlFreeDoc(response); return false; } } else if (attr_is_equal_ex(attr,"encodingStyle",SOAP_1_2_ENV_NAMESPACE)) { if (soap_version == SOAP_1_2) { add_soap_fault(obj, "Client", "encodingStyle cannot be specified on the Body"); xmlFreeDoc(response); return false; } if (strcmp((char*)attr->children->content, SOAP_1_1_ENC_NAMESPACE)) { add_soap_fault(obj, "Client", "Unknown data encoding style"); xmlFreeDoc(response); return false; } } attr = attr->next; } if (trav != nullptr && soap_version == SOAP_1_2) { add_soap_fault(obj, "Client", "A SOAP 1.2 envelope can contain only Header and Body"); xmlFreeDoc(response); return false; } if (head != nullptr) { attr = head->properties; while (attr != nullptr) { if (attr->ns == nullptr) { add_soap_fault(obj, "Client", "A SOAP Header element cannot have non Namespace " "qualified attributes"); xmlFreeDoc(response); return false; } if (attr_is_equal_ex(attr, "encodingStyle", SOAP_1_2_ENV_NAMESPACE)) { if (soap_version == SOAP_1_2) { add_soap_fault(obj, "Client", "encodingStyle cannot be specified on the Header"); xmlFreeDoc(response); return false; } if (strcmp((char*)attr->children->content, SOAP_1_1_ENC_NAMESPACE)) { add_soap_fault(obj, "Client", "Unknown data encoding style"); xmlFreeDoc(response); return false; } } attr = attr->next; } } /* Check if <Body> contains <Fault> element */ fault = get_node_ex(body->children,"Fault",envelope_ns); if (fault != nullptr) { char *faultcode = nullptr; String faultstring, faultactor; Variant details; xmlNodePtr tmp; if (soap_version == SOAP_1_1) { tmp = get_node(fault->children, "faultcode"); if (tmp != nullptr && tmp->children != nullptr) { faultcode = (char*)tmp->children->content; } tmp = get_node(fault->children, "faultstring"); if (tmp != nullptr && tmp->children != nullptr) { Variant zv = master_to_zval(get_conversion(KindOfString), tmp); faultstring = zv.toString(); } tmp = get_node(fault->children, "faultactor"); if (tmp != nullptr && tmp->children != nullptr) { Variant zv = master_to_zval(get_conversion(KindOfString), tmp); faultactor = zv.toString(); } tmp = get_node(fault->children, "detail"); if (tmp != nullptr) { details = master_to_zval(encodePtr(), tmp); } } else { tmp = get_node(fault->children, "Code"); if (tmp != nullptr && tmp->children != nullptr) { tmp = get_node(tmp->children, "Value"); if (tmp != nullptr && tmp->children != nullptr) { faultcode = (char*)tmp->children->content; } } tmp = get_node(fault->children,"Reason"); if (tmp != nullptr && tmp->children != nullptr) { /* TODO: lang attribute */ tmp = get_node(tmp->children,"Text"); if (tmp != nullptr && tmp->children != nullptr) { Variant zv = master_to_zval(get_conversion(KindOfString), tmp); faultstring = zv.toString(); } } tmp = get_node(fault->children,"Detail"); if (tmp != nullptr) { details = master_to_zval(encodePtr(), tmp); } } obj->m_soap_fault = SystemLib::AllocSoapFaultObject(String(faultcode, CopyString), faultstring, faultactor, details); xmlFreeDoc(response); return false; } /* Parse content of <Body> element */ return_value = Array::Create(); resp = body->children; while (resp != nullptr && resp->type != XML_ELEMENT_NODE) { resp = resp->next; } if (resp != nullptr) { if (fn && fn->binding && fn->binding->bindingType == BINDING_SOAP) { /* Function has WSDL description */ sdlParamPtr h_param, param; xmlNodePtr val = nullptr; const char *name, *ns = nullptr; Variant tmp(Variant::NullInit{}); sdlSoapBindingFunctionPtr fnb = (sdlSoapBindingFunctionPtr)fn->bindingAttributes; int res_count; hdrs = &fnb->output.headers; if (!fn->responseParameters.empty()) { res_count = fn->responseParameters.size(); for (unsigned int i = 0; i < fn->responseParameters.size(); i++) { h_param = fn->responseParameters[i]; param = h_param; if (fnb->style == SOAP_DOCUMENT) { if (param->element) { name = param->element->name.c_str(); ns = param->element->namens.c_str(); /* name = param->encode->details.type_str; ns = param->encode->details.ns; */ } else { name = param->paramName.c_str(); } } else { name = fn->responseName.c_str(); /* ns = ? */ } /* Get value of parameter */ cur = get_node_ex(resp, (char*)name, (char*)ns); if (!cur) { cur = get_node(resp, (char*)name); /* TODO: produce warning invalid ns */ } if (!cur && fnb->style == SOAP_RPC) { cur = resp; } if (cur) { if (fnb->style == SOAP_DOCUMENT) { val = cur; } else { val = get_node(cur->children, (char*)param->paramName.c_str()); if (res_count == 1) { if (val == nullptr) { val = get_node(cur->children, "return"); } if (val == nullptr) { val = get_node(cur->children, "result"); } if (val == nullptr && cur->children && !cur->children->next) { val = cur->children; } } } } if (!val) { /* TODO: may be "nil" is not OK? */ /* add_soap_fault(obj, "Client", "Can't find response data"); xmlFreeDoc(response); return false; */ } else { /* Decoding value of parameter */ if (param != nullptr) { tmp = master_to_zval(param->encode, val); } else { tmp = master_to_zval(encodePtr(), val); } } return_value.toArrRef().set(String(param->paramName), tmp); param_count++; } } } else { /* Function hasn't WSDL description */ xmlNodePtr val; val = resp->children; while (val != nullptr) { while (val && val->type != XML_ELEMENT_NODE) { val = val->next; } if (val != nullptr) { if (!node_is_equal_ex(val,"result",RPC_SOAP12_NAMESPACE)) { Variant tmp = master_to_zval(encodePtr(), val); if (val->name) { String key((char*)val->name, CopyString); if (return_value.toCArrRef().exists(key)) { auto& lval = return_value.toArrRef().lvalAt(key); if (!lval.isArray()) lval = lval.toArray(); lval.toArrRef().append(tmp); } else if (val->next && get_node(val->next, (char*)val->name)) { Array arr = Array::Create(); arr.append(tmp); return_value.toArrRef().set(key, arr); } else { return_value.toArrRef().set(key, tmp); } } else { return_value.toArrRef().append(tmp); } ++param_count; } val = val->next; } } } } if (return_value.isArray()) { if (param_count == 0) { return_value = init_null(); } else if (param_count == 1) { Array arr = return_value.toArray(); ArrayIter iter(arr); return_value = iter.second(); } } if (head) { trav = head->children; while (trav) { if (trav->type == XML_ELEMENT_NODE) { encodePtr enc; if (hdrs && !hdrs->empty()) { std::string key; if (trav->ns) { key += (char*)trav->ns->href; key += ':'; } key += (char*)trav->name; sdlSoapBindingFunctionHeaderMap::const_iterator iter = hdrs->find(key); if (iter != hdrs->end()) { enc = iter->second->encode; } } soap_headers.set(String((char*)trav->name, CopyString), master_to_zval(enc, trav)); } trav = trav->next; } } xmlFreeDoc(response); return true; }
// Complex arithmetics with brackets bool Expression::complexArithmetic(Stack &stack, StackFrame &stackFrame, int16 brackStart) { switch (stackFrame.opers[-2]) { case OP_ADD: if (stack.opers[brackStart] == OP_LOAD_IMM_INT16) { stack.values[brackStart] += stackFrame.values[-1]; } else if (stack.opers[brackStart] == OP_LOAD_IMM_STR) { if ((char *)decodePtr(stack.values[brackStart]) != _resultStr) { strcpy(_resultStr, (char *)decodePtr(stack.values[brackStart])); stack.values[brackStart] = encodePtr((byte *)_resultStr, kResStr); } strcat(_resultStr, (char *)decodePtr(stackFrame.values[-1])); } stackFrame.pop(2); break; case OP_SUB: stack.values[brackStart] -= stackFrame.values[-1]; stackFrame.pop(2); break; case OP_BITOR: stack.values[brackStart] |= stackFrame.values[-1]; stackFrame.pop(2); break; case OP_MUL: stackFrame.values[-3] *= stackFrame.values[-1]; stackFrame.pop(2); break; case OP_DIV: stackFrame.values[-3] /= stackFrame.values[-1]; stackFrame.pop(2); break; case OP_MOD: stackFrame.values[-3] %= stackFrame.values[-1]; stackFrame.pop(2); break; case OP_BITAND: stackFrame.values[-3] &= stackFrame.values[-1]; stackFrame.pop(2); break; case OP_OR: // (x OR false) == x // (x OR true) == true if (stackFrame.opers[-3] == GOB_FALSE) stackFrame.opers[-3] = stackFrame.opers[-1]; stackFrame.pop(2); break; case OP_AND: // (x AND false) == false // (x AND true) == x if (stackFrame.opers[-3] == GOB_TRUE) stackFrame.opers[-3] = stackFrame.opers[-1]; stackFrame.pop(2); break; case OP_LESS: stackFrame.opers[-3] = (cmpHelper(stackFrame) < 0) ? GOB_TRUE : GOB_FALSE; stackFrame.pop(2); break; case OP_LEQ: stackFrame.opers[-3] = (cmpHelper(stackFrame) <= 0) ? GOB_TRUE : GOB_FALSE; stackFrame.pop(2); break; case OP_GREATER: stackFrame.opers[-3] = (cmpHelper(stackFrame) > 0) ? GOB_TRUE : GOB_FALSE; stackFrame.pop(2); break; case OP_GEQ: stackFrame.opers[-3] = (cmpHelper(stackFrame) >= 0) ? GOB_TRUE : GOB_FALSE; stackFrame.pop(2); break; case OP_EQ: stackFrame.opers[-3] = (cmpHelper(stackFrame) == 0) ? GOB_TRUE : GOB_FALSE; stackFrame.pop(2); break; case OP_NEQ: stackFrame.opers[-3] = (cmpHelper(stackFrame) != 0) ? GOB_TRUE : GOB_FALSE; stackFrame.pop(2); break; default: return true; } return false; }
// Load a value according to the operation void Expression::loadValue(byte operation, uint32 varBase, const StackFrame &stackFrame) { int16 dimCount; uint16 temp; int16 temp2; int16 offset; int16 dim; byte *arrDescPtr; int32 prevPrevVal; int32 prevVal; int32 curVal; switch (operation) { case OP_ARRAY_INT8: case OP_ARRAY_INT32: case OP_ARRAY_INT16: case OP_ARRAY_STR: *stackFrame.opers = (operation == OP_ARRAY_STR) ? OP_LOAD_IMM_STR : OP_LOAD_IMM_INT16; temp = _vm->_game->_script->readInt16(); dimCount = _vm->_game->_script->readByte(); arrDescPtr = _vm->_game->_script->getData() + _vm->_game->_script->pos(); _vm->_game->_script->skip(dimCount); offset = 0; for (dim = 0; dim < dimCount; dim++) { temp2 = parseValExpr(OP_END_MARKER); offset = offset * arrDescPtr[dim] + temp2; } if (operation == OP_ARRAY_INT8) *stackFrame.values = (int8) READ_VARO_UINT8(varBase + temp + offset); else if (operation == OP_ARRAY_INT32) *stackFrame.values = READ_VARO_UINT32(varBase + temp * 4 + offset * 4); else if (operation == OP_ARRAY_INT16) *stackFrame.values = (int16) READ_VARO_UINT16(varBase + temp * 2 + offset * 2); else if (operation == OP_ARRAY_STR) { *stackFrame.values = encodePtr(_vm->_inter->_variables->getAddressOff8( varBase + temp * 4 + offset * _vm->_global->_inter_animDataSize * 4), kInterVar); if (_vm->_game->_script->peekByte() == 13) { _vm->_game->_script->skip(1); temp2 = parseValExpr(OP_END_MARKER); *stackFrame.opers = OP_LOAD_IMM_INT16; *stackFrame.values = READ_VARO_UINT8(varBase + temp * 4 + offset * 4 * _vm->_global->_inter_animDataSize + temp2); } } break; case OP_LOAD_VAR_INT16: *stackFrame.opers = OP_LOAD_IMM_INT16; *stackFrame.values = (int16) READ_VARO_UINT16(varBase + _vm->_game->_script->readUint16() * 2); break; case OP_LOAD_VAR_INT8: *stackFrame.opers = OP_LOAD_IMM_INT16; *stackFrame.values = (int8) READ_VARO_UINT8(varBase + _vm->_game->_script->readUint16()); break; case OP_LOAD_IMM_INT32: *stackFrame.opers = OP_LOAD_IMM_INT16; *stackFrame.values = _vm->_game->_script->readInt32(); break; case OP_LOAD_IMM_INT16: *stackFrame.opers = OP_LOAD_IMM_INT16; *stackFrame.values = _vm->_game->_script->readInt16(); break; case OP_LOAD_IMM_INT8: *stackFrame.opers = OP_LOAD_IMM_INT16; *stackFrame.values = _vm->_game->_script->readInt8(); break; case OP_LOAD_IMM_STR: *stackFrame.opers = OP_LOAD_IMM_STR; *stackFrame.values = encodePtr((byte *)_vm->_game->_script->readString(), kExecPtr); break; case OP_LOAD_VAR_INT32: *stackFrame.opers = OP_LOAD_IMM_INT16; *stackFrame.values = READ_VARO_UINT32(varBase + _vm->_game->_script->readUint16() * 4); break; case OP_LOAD_VAR_INT32_AS_INT16: *stackFrame.opers = OP_LOAD_IMM_INT16; *stackFrame.values = (int16) READ_VARO_UINT16(varBase + _vm->_game->_script->readUint16() * 4); break; case OP_LOAD_VAR_STR: *stackFrame.opers = OP_LOAD_IMM_STR; temp = _vm->_game->_script->readUint16() * 4; *stackFrame.values = encodePtr(_vm->_inter->_variables->getAddressOff8(varBase + temp), kInterVar); if (_vm->_game->_script->peekByte() == 13) { _vm->_game->_script->skip(1); temp += parseValExpr(OP_END_MARKER); *stackFrame.opers = OP_LOAD_IMM_INT16; *stackFrame.values = READ_VARO_UINT8(varBase + temp); } break; case OP_FUNC: operation = _vm->_game->_script->readByte(); parseExpr(OP_END_EXPR, 0); switch (operation) { case FUNC_SQRT1: case FUNC_SQRT2: case FUNC_SQRT3: curVal = 1; prevVal = 1; do { prevPrevVal = prevVal; prevVal = curVal; curVal = (curVal + _resultInt / curVal) / 2; } while ((curVal != prevVal) && (curVal != prevPrevVal)); _resultInt = curVal; break; case FUNC_SQR: _resultInt = _resultInt * _resultInt; break; case FUNC_ABS: if (_resultInt < 0) _resultInt = -_resultInt; break; case FUNC_RAND: _resultInt = _vm->_util->getRandom(_resultInt); break; } *stackFrame.opers = OP_LOAD_IMM_INT16; *stackFrame.values = _resultInt; break; } }
int16 Expression::parseExpr(byte stopToken, byte *type) { Stack stack; StackFrame stackFrame(stack); byte operation; int16 brackStart; uint32 varBase; while (true) { getVarBase(varBase); stackFrame.push(); operation = _vm->_game->_script->readByte(); if ((operation >= OP_ARRAY_INT8) && (operation <= OP_FUNC)) { loadValue(operation, varBase, stackFrame); if ((stackFrame.pos > 0) && ((stackFrame.opers[-1] == OP_NEG) || (stackFrame.opers[-1] == OP_NOT))) { stackFrame.pop(); if (*stackFrame.opers == OP_NEG) { *stackFrame.opers = OP_LOAD_IMM_INT16; stackFrame.values[0] = -stackFrame.values[1]; } else *stackFrame.opers = (stackFrame.opers[1] == GOB_FALSE) ? GOB_TRUE : GOB_FALSE; } if (stackFrame.pos <= 0) continue; simpleArithmetic1(stackFrame); continue; } // (op >= OP_ARRAY_INT8) && (op <= OP_FUNC) if ((operation == stopToken) || (operation == OP_OR) || (operation == OP_AND) || (operation == OP_END_EXPR)) { while (stackFrame.pos >= 2) { if ((stackFrame.opers[-2] == OP_BEGIN_EXPR) && ((operation == OP_END_EXPR) || (operation == stopToken))) { stackFrame.opers[-2] = stackFrame.opers[-1]; if ((stackFrame.opers[-2] == OP_LOAD_IMM_INT16) || (stackFrame.opers[-2] == OP_LOAD_IMM_STR)) stackFrame.values[-2] = stackFrame.values[-1]; stackFrame.pop(); simpleArithmetic2(stackFrame); if (operation != stopToken) break; } // if ((stackFrame.opers[-2] == OP_BEGIN_EXPR) && ...) for (brackStart = (stackFrame.pos - 2); (brackStart > 0) && (stack.opers[brackStart] < OP_OR) && (stack.opers[brackStart] != OP_BEGIN_EXPR); brackStart--) ; if ((stack.opers[brackStart] >= OP_OR) || (stack.opers[brackStart] == OP_BEGIN_EXPR)) brackStart++; if (complexArithmetic(stack, stackFrame, brackStart)) break; } // while (stackFrame.pos >= 2) if ((operation == OP_OR) || (operation == OP_AND)) { if (stackFrame.opers[-1] == OP_LOAD_IMM_INT16) { if (stackFrame.values[-1] != 0) stackFrame.opers[-1] = GOB_TRUE; else stackFrame.opers[-1] = GOB_FALSE; } if (((operation == OP_OR) && (stackFrame.opers[-1] == GOB_TRUE)) || ((operation == OP_AND) && (stackFrame.opers[-1] == GOB_FALSE))) { if ((stackFrame.pos > 1) && (stackFrame.opers[-2] == OP_BEGIN_EXPR)) { skipExpr(OP_END_EXPR); stackFrame.opers[-2] = stackFrame.opers[-1]; stackFrame.pop(2); } else { skipExpr(stopToken); } operation = _vm->_game->_script->peekByte(-1); if ((stackFrame.pos > 0) && (stackFrame.opers[-1] == OP_NOT)) { if (stackFrame.opers[0] == GOB_FALSE) stackFrame.opers[-1] = GOB_TRUE; else stackFrame.opers[-1] = GOB_FALSE; stackFrame.pop(); } } else stackFrame.opers[0] = operation; } else stackFrame.pop(); if (operation != stopToken) continue; getResult(stack.opers[0], stack.values[0], type); return 0; } // (operation == stopToken) || (operation == OP_OR) || (operation == OP_AND) || (operation == OP_END_EXPR) if ((operation < OP_NEG) || (operation > OP_NOT)) { if ((operation < OP_LESS) || (operation > OP_NEQ)) continue; if (stackFrame.pos > 2) { if (stackFrame.opers[-2] == OP_ADD) { if (stackFrame.opers[-3] == OP_LOAD_IMM_INT16) { stackFrame.values[-3] += stackFrame.values[-1]; } else if (stackFrame.opers[-3] == OP_LOAD_IMM_STR) { if ((char *)decodePtr(stackFrame.values[-3]) != _resultStr) { strcpy(_resultStr, (char *)decodePtr(stackFrame.values[-3])); stackFrame.values[-3] = encodePtr((byte *)_resultStr, kResStr); } strcat(_resultStr, (char *)decodePtr(stackFrame.values[-1])); } stackFrame.pop(2); } else if (stackFrame.opers[-2] == OP_SUB) { stackFrame.values[-3] -= stackFrame.values[-1]; stackFrame.pop(2); } else if (stackFrame.opers[-2] == OP_BITOR) { stackFrame.values[-3] |= stackFrame.values[-1]; stackFrame.pop(2); } } } *stackFrame.opers = operation; } }