bool UnaryOpExpression::preCompute(CVarRef value, Variant &result) { bool ret = true; try { g_context->setThrowAllErrors(true); switch(m_op) { case '!': result = (!toBoolean(value)); break; case '+': cellSet(cellAdd(make_tv<KindOfInt64>(0), *value.asCell()), *result.asCell()); break; case '-': cellSet(cellSub(make_tv<KindOfInt64>(0), *value.asCell()), *result.asCell()); break; case '~': tvSet(*value.asCell(), *result.asTypedValue()); cellBitNot(*result.asCell()); break; case '@': result = value; break; case T_INT_CAST: result = value.toInt64(); break; case T_DOUBLE_CAST: result = toDouble(value); break; case T_STRING_CAST: result = toString(value); break; case T_BOOL_CAST: result = toBoolean(value); break; case T_EMPTY: result = !toBoolean(value); break; case T_ISSET: result = is_not_null(value); break; case T_INC: case T_DEC: assert(false); default: ret = false; break; } } catch (...) { ret = false; } g_context->setThrowAllErrors(false); return ret; }
TEST(APC, SetOverwrite) { auto store = new_store(); store->set(s_key, Variant(s_value1), 1500); Variant got; EXPECT_EQ(store->get(s_key, got), true); EXPECT_TRUE(cellSame(*got.asCell(), make_tv<KindOfStaticString>(s_value1.get()))); store->set(s_key, Variant(s_value2), 1500); EXPECT_EQ(store->get(s_key, got), true); EXPECT_TRUE(cellSame(*got.asCell(), make_tv<KindOfStaticString>(s_value2.get()))); }
Variant f_max(int _argc, const Variant& value, const Variant& second /* = null_variant */, const Array& _argv /* = null_array */) { if (_argc == 1) { const auto& cell_value = *value.asCell(); if (UNLIKELY(!isContainer(cell_value))) { return value; } ArrayIter iter(cell_value); if (!iter) { return uninit_null(); } Variant ret = iter.secondRefPlus(); ++iter; for (; iter; ++iter) { Variant currVal = iter.secondRefPlus(); if (more(currVal, ret)) { ret = currVal; } } return ret; } else if (_argc == 2) { return more(second, value) ? second : value; } Variant ret = more(second, value) ? second : value; for (ArrayIter iter(_argv); iter; ++iter) { Variant currVal = iter.secondRef(); if (more(currVal, ret)) { ret = currVal; } } return ret; }
bool objOffsetIsset(TypedValue& tvRef, ObjectData* base, const Variant& offset, bool validate /* = true */) { auto exists = objOffsetExists(base, offset); // Unless we called ArrayObject::offsetExists, there's nothing more to do if (exists != OffsetExistsResult::IssetIfNonNull) { return (int)exists; } // For ArrayObject::offsetExists, we need to check the value at `offset`. // If it's null, then we return false. TypedValue tvResult; tvWriteUninit(&tvResult); // We can't call the offsetGet method on `base` because users aren't // expecting offsetGet to be called for `isset(...)` expressions, so call // the method on the base ArrayObject class. auto const method = SystemLib::s_ArrayObjectClass->lookupMethod(s_offsetGet.get()); assert(method != nullptr); g_context->invokeFuncFew(&tvResult, method, base, nullptr, 1, offset.asCell()); auto const result = !IS_NULL_TYPE(tvResult.m_type); tvRefcountedDecRef(&tvResult); return result; }
bool same(const Variant& v1, int64_t v2) { auto const cell = v1.asCell(); if (isIntType(cell->m_type)) { return v2 == cell->m_data.num; } return false; }
bool HHVM_FUNCTION(define, const String& name, const Variant& value, bool case_insensitive /* = false */) { if (case_insensitive) { raise_warning(Strings::CONSTANTS_CASE_SENSITIVE); } return Unit::defCns(name.get(), value.asCell()); }
void StringBuffer::append(const Variant& v) { auto const cell = v.asCell(); switch (cell->m_type) { case KindOfInt64: append(cell->m_data.num); break; case KindOfPersistentString: case KindOfString: append(cell->m_data.pstr); break; case KindOfUninit: case KindOfNull: case KindOfBoolean: case KindOfDouble: case KindOfPersistentVec: case KindOfVec: case KindOfPersistentDict: case KindOfDict: case KindOfPersistentKeyset: case KindOfKeyset: case KindOfPersistentArray: case KindOfArray: case KindOfObject: case KindOfResource: case KindOfRef: append(v.toString()); } }
void objOffsetUnset(ObjectData* base, const Variant& offset) { objArrayAccess(base); assert(!base->isCollection()); const Func* method = base->methodNamed(s_offsetUnset.get()); assert(method != nullptr); TypedValue tv; tvWriteUninit(&tv); g_context->invokeFuncFew(&tv, method, base, nullptr, 1, offset.asCell()); tvRefcountedDecRef(&tv); }
AutoloadHandler::Result AutoloadHandler::loadFromMap(const String& name, const String& kind, bool toLower, const T &checkExists) { assert(!m_map.isNull()); while (true) { const Variant& type_map = m_map.get()->get(kind); auto const typeMapCell = type_map.asCell(); if (typeMapCell->m_type != KindOfArray) return Failure; String canonicalName = toLower ? f_strtolower(name) : name; const Variant& file = typeMapCell->m_data.parr->get(canonicalName); bool ok = false; if (file.isString()) { String fName = file.toCStrRef().get(); if (fName.get()->data()[0] != '/') { if (!m_map_root.empty()) { fName = m_map_root + fName; } } try { JIT::VMRegAnchor _; bool initial; auto const ec = g_context.getNoCheck(); Unit* u = ec->evalInclude(fName.get(), nullptr, &initial); if (u) { if (initial) { TypedValue retval; ec->invokeFunc(&retval, u->getMain(), init_null_variant, nullptr, nullptr, nullptr, nullptr, ExecutionContext::InvokePseudoMain); tvRefcountedDecRef(&retval); } ok = true; } } catch (...) {} } if (ok && checkExists(name)) { return Success; } const Variant& func = m_map.get()->get(s_failure); if (func.isNull()) return Failure; // can throw, otherwise // - true means the map was updated. try again // - false means we should stop applying autoloaders (only affects classes) // - anything else means keep going Variant action = vm_call_user_func(func, make_packed_array(kind, name)); auto const actionCell = action.asCell(); if (actionCell->m_type == KindOfBoolean) { if (actionCell->m_data.num) continue; return StopAutoloading; } return ContinueAutoloading; } }
void c_ConditionWaitHandle::t_succeed(const Variant& result) { if (isFinished()) { failAlreadyFinished(); } assert(getState() == STATE_BLOCKED); auto parentChain = getParentChain(); setState(STATE_SUCCEEDED); cellDup(*result.asCell(), m_resultOrException); parentChain.unblock(); }
TEST(APC, Basic) { auto store = new_store(); EXPECT_EQ(store->add(s_key, Variant(s_value1), 1500), true); EXPECT_EQ(store->exists(s_key), true); Variant got; EXPECT_EQ(store->get(s_key, got), true); EXPECT_TRUE(cellSame(*got.asCell(), make_tv<KindOfStaticString>(s_value1.get()))); EXPECT_EQ(store->erase(s_key), true); EXPECT_EQ(store->get(s_key, got), false); }
bool EventHook::RunInterceptHandler(ActRec* ar) { const Func* func = ar->m_func; if (LIKELY(func->maybeIntercepted() == 0)) return true; // Intercept only original generator / async function calls, not resumption. if (ar->inGenerator()) return true; Variant *h = get_intercept_handler(func->fullNameRef(), &func->maybeIntercepted()); if (!h) return true; JIT::VMRegAnchor _; PC savePc = g_context->m_pc; Variant doneFlag = true; Variant called_on; if (ar->hasThis()) { called_on = Variant(ar->getThis()); } else if (ar->hasClass()) { // For static methods, give handler the name of called class called_on = Variant(const_cast<StringData*>(ar->getClass()->name())); } Variant intArgs = PackedArrayInit(5) .append(ar->m_func->fullNameRef()) .append(called_on) .append(get_frame_args_with_ref(ar)) .append(h->asCArrRef()[1]) .appendRef(doneFlag) .toArray(); Variant ret = vm_call_user_func(h->asCArrRef()[0], intArgs); if (doneFlag.toBoolean()) { Offset pcOff; ActRec* outer = g_context->getPrevVMState(ar, &pcOff); frame_free_locals_inl_no_hook<true>(ar, ar->m_func->numLocals()); Stack& stack = g_context->getStack(); stack.top() = (Cell*)(ar + 1); cellDup(*ret.asCell(), *stack.allocTV()); g_context->m_fp = outer; g_context->m_pc = outer ? outer->m_func->unit()->at(pcOff) : nullptr; return false; } g_context->m_fp = ar; g_context->m_pc = savePc; return true; }
void HHVM_METHOD(ConditionWaitHandle, succeed, const Variant& result) { auto obj = wait_handle<c_ConditionWaitHandle>(this_); if (obj->isFinished()) { failAlreadyFinished(); } assert(obj->getState() == c_ConditionWaitHandle::STATE_BLOCKED); auto parentChain = obj->getParentChain(); obj->setState(c_ConditionWaitHandle::STATE_SUCCEEDED); cellDup(*result.asCell(), obj->m_resultOrException); parentChain.unblock(); }
void SourceRootInfo::setServerVariables(Variant &server) const { if (!sandboxOn()) return; for (map<string, string>::const_iterator it = RuntimeOption::SandboxServerVariables.begin(); it != RuntimeOption::SandboxServerVariables.end(); ++it) { server.set(String(it->first), String(parseSandboxServerVariable(it->second))); } if (!m_serverVars.empty()) { cellAddEq(*server.asCell(), make_tv<KindOfArray>(m_serverVars.get())); } }
void throw_invalid_object_type(const Variant& p) { auto tv = p.asCell(); switch (tv->m_type) { case KindOfNull: case KindOfUninit: throw_null_pointer_exception(); case KindOfObject: throw_invalid_object_type(tv->m_data.pobj->getClassName().c_str()); case KindOfResource: throw_invalid_object_type(tv->m_data.pres->o_getClassName().c_str()); default: throw_invalid_object_type(tname(tv->m_type).c_str()); } }
void objOffsetSet(ObjectData* base, const Variant& offset, TypedValue* val, bool validate /* = true */) { if (validate) { objArrayAccess(base); } assert(!base->isCollection()); const Func* method = base->methodNamed(s_offsetSet.get()); assert(method != nullptr); TypedValue tvResult; tvWriteUninit(&tvResult); TypedValue args[2] = { *offset.asCell(), *tvToCell(val) }; g_context->invokeFuncFew(&tvResult, method, base, nullptr, 2, args); tvRefcountedDecRef(&tvResult); }
void StringBuffer::append(const Variant& v) { auto const cell = v.asCell(); switch (cell->m_type) { case KindOfStaticString: case KindOfString: append(cell->m_data.pstr); break; case KindOfInt64: append(cell->m_data.num); break; default: append(v.toString()); } }
static OffsetExistsResult objOffsetExists(ObjectData* base, const Variant& offset) { objArrayAccess(base); TypedValue tvResult; tvWriteUninit(&tvResult); assert(!base->isCollection()); const Func* method = base->methodNamed(s_offsetExists.get()); assert(method != nullptr); g_context->invokeFuncFew(&tvResult, method, base, nullptr, 1, offset.asCell()); tvCastToBooleanInPlace(&tvResult); if (!tvResult.m_data.num) return OffsetExistsResult::DoesNotExist; return method->cls() == SystemLib::s_ArrayObjectClass ? OffsetExistsResult::IssetIfNonNull : OffsetExistsResult::DefinitelyExists; }
bool EventHook::RunInterceptHandler(ActRec* ar) { const Func* func = ar->m_func; if (LIKELY(func->maybeIntercepted() == 0)) return true; Variant *h = get_intercept_handler(func->fullNameRef(), &func->maybeIntercepted()); if (!h) return true; Transl::VMRegAnchor _; PC savePc = g_vmContext->m_pc; Variant doneFlag = true; Variant called_on; if (ar->hasThis()) { called_on = Variant(ar->getThis()); } else if (ar->hasClass()) { // For static methods, give handler the name of called class called_on = Variant(const_cast<StringData*>(ar->getClass()->name())); } Array intArgs = CREATE_VECTOR5(ar->m_func->fullNameRef(), called_on, get_frame_args_with_ref(ar), h->asCArrRef()[1], ref(doneFlag)); Variant ret = vm_call_user_func(h->asCArrRef()[0], intArgs); if (doneFlag.toBoolean()) { Offset pcOff; ActRec* outer = g_vmContext->getPrevVMState(ar, &pcOff); frame_free_locals_inl_no_hook<true>(ar, ar->m_func->numLocals()); Stack& stack = g_vmContext->getStack(); stack.top() = (Cell*)(ar + 1); cellDup(*ret.asCell(), *stack.allocTV()); g_vmContext->m_fp = outer; g_vmContext->m_pc = outer ? outer->m_func->unit()->at(pcOff) : nullptr; return false; } g_vmContext->m_fp = ar; g_vmContext->m_pc = savePc; return true; }
bool UnaryOpExpression::preCompute(const Variant& value, Variant &result) { bool ret = true; try { ThrowAllErrorsSetter taes; auto add = RuntimeOption::IntsOverflowToInts ? cellAdd : cellAddO; auto sub = RuntimeOption::IntsOverflowToInts ? cellSub : cellSubO; switch(m_op) { case '!': result = (!toBoolean(value)); break; case '+': cellSet(add(make_tv<KindOfInt64>(0), *value.asCell()), *result.asCell()); break; case '-': cellSet(sub(make_tv<KindOfInt64>(0), *value.asCell()), *result.asCell()); break; case '~': tvSet(*value.asCell(), *result.asTypedValue()); cellBitNot(*result.asCell()); break; case '@': result = value; break; case T_INT_CAST: result = value.toInt64(); break; case T_DOUBLE_CAST: result = toDouble(value); break; case T_STRING_CAST: result = toString(value); break; case T_BOOL_CAST: result = toBoolean(value); break; case T_EMPTY: result = !toBoolean(value); break; case T_ISSET: result = is_not_null(value); break; case T_INC: case T_DEC: assert(false); default: ret = false; break; } } catch (...) { ret = false; } return ret; }
TEST(APC, BasicPrimeStuff) { auto store = new_primed_store(); Variant val; EXPECT_TRUE(store->get("int_2", val)); EXPECT_TRUE(cellSame(*val.asCell(), make_tv<KindOfInt64>(2))); bool found = false; EXPECT_EQ(store->inc("int_3", 1, found), 4); EXPECT_TRUE(found); EXPECT_FALSE(store->get("int_200", val)); EXPECT_EQ(store->cas("obj_1", 1, 2), true); // stdclass converts to 1 EXPECT_EQ(store->cas("obj_2", 4, 5), false); EXPECT_EQ(store->cas("int_4", 4, 5), true); EXPECT_EQ(store->cas("int_5", 4, 5), false); }
bool contains(ObjectData* obj, const Variant& offset) { auto* key = offset.asCell(); switch (obj->collectionType()) { case CollectionType::Vector: case CollectionType::ImmVector: return BaseVector::OffsetContains(obj, key); case CollectionType::Map: case CollectionType::ImmMap: return BaseMap::OffsetContains(obj, key); case CollectionType::Set: case CollectionType::ImmSet: return BaseSet::OffsetContains(obj, key); case CollectionType::Pair: return c_Pair::OffsetContains(obj, key); } not_reached(); }
void HHVM_FUNCTION(set_frame_metadata, const Variant& metadata) { VMRegAnchor _; auto fp = vmfp(); if (fp && fp->skipFrame()) fp = g_context->getPrevVMState(fp); if (UNLIKELY(!fp)) return; if (LIKELY(!(fp->func()->attrs() & AttrMayUseVV)) || LIKELY(!fp->hasVarEnv())) { auto const local = fp->func()->lookupVarId(s_86metadata.get()); if (LIKELY(local != kInvalidId)) { cellSet(*metadata.asCell(), *tvAssertCell(frame_local(fp, local))); } else { SystemLib::throwInvalidArgumentExceptionObject( "Unsupported dynamic call of set_frame_metadata()"); } } else { fp->getVarEnv()->set(s_86metadata.get(), metadata.asTypedValue()); } }
void c_ConditionWaitHandle::t_fail(const Variant& exception) { auto const cell = exception.asCell(); if (UNLIKELY( cell->m_type != KindOfObject || !cell->m_data.pobj->instanceof(SystemLib::s_ExceptionClass) )) { SystemLib::throwInvalidArgumentExceptionObject( "Expected exception to be an instance of Exception"); } if (isFinished()) { failAlreadyFinished(); } assert(getState() == STATE_BLOCKED); auto parentChain = getParentChain(); setState(STATE_FAILED); cellDup(make_tv<KindOfObject>(cell->m_data.pobj), m_resultOrException); parentChain.unblock(); }
Object c_ConditionWaitHandle::ti_create(const Variant& child) { // Child not a WaitHandle? auto const child_wh = c_WaitHandle::fromCell(child.asCell()); if (UNLIKELY(!child_wh)) { SystemLib::throwInvalidArgumentExceptionObject( "Expected child to be an instance of WaitHandle"); } // Child finished before notification? if (UNLIKELY(child_wh->isFinished())) { throwNotNotifiedException(); } assert(child_wh->instanceof(c_WaitableWaitHandle::classof())); auto const child_wwh = static_cast<c_WaitableWaitHandle*>(child_wh); auto wh = req::make<c_ConditionWaitHandle>(); wh->initialize(child_wwh); return Object(std::move(wh)); }
void StringBuffer::append(const Variant& v) { auto const cell = v.asCell(); switch (cell->m_type) { case KindOfInt64: append(cell->m_data.num); break; case KindOfStaticString: case KindOfString: append(cell->m_data.pstr); break; case KindOfUninit: case KindOfNull: case KindOfBoolean: case KindOfDouble: case KindOfArray: case KindOfObject: case KindOfResource: case KindOfRef: case KindOfClass: append(v.toString()); } }
ALWAYS_INLINE typename std::enable_if< std::is_base_of<BaseMap, TMap>::value, Object>::type BaseMap::FromItems(const Class*, const Variant& iterable) { if (iterable.isNull()) return Object{req::make<TMap>()}; VMRegGuard _; size_t sz; ArrayIter iter = getArrayIterHelper(iterable, sz); auto target = req::make<TMap>(); target->reserve(sz); for (; iter; ++iter) { Variant v = iter.second(); TypedValue* tv = v.asCell(); if (UNLIKELY(tv->m_type != KindOfObject || tv->m_data.pobj->getVMClass() != c_Pair::classof())) { SystemLib::throwInvalidArgumentExceptionObject( "Parameter must be an instance of Iterable<Pair>"); } auto pair = static_cast<c_Pair*>(tv->m_data.pobj); target->setRaw(&pair->elm0, &pair->elm1); } return Object{std::move(target)}; }
bool EventHook::RunInterceptHandler(ActRec* ar) { const Func* func = ar->func(); if (LIKELY(func->maybeIntercepted() == 0)) return true; // Intercept only original generator / async function calls, not resumption. if (ar->resumed()) return true; Variant* h = get_intercept_handler(func->fullNameStr(), &func->maybeIntercepted()); if (!h) return true; /* * In production mode, only functions that we have assumed can be * intercepted during static analysis should actually be * intercepted. */ if (RuntimeOption::RepoAuthoritative && !RuntimeOption::EvalJitEnableRenameFunction) { if (!(func->attrs() & AttrInterceptable)) { raise_error("fb_intercept was used on a non-interceptable function (%s) " "in RepoAuthoritative mode", func->fullName()->data()); } } VMRegAnchor _; PC savePc = vmpc(); Variant doneFlag = true; Variant called_on; if (ar->hasThis()) { called_on = Variant(ar->getThis()); } else if (ar->hasClass()) { // For static methods, give handler the name of called class called_on = Variant(const_cast<StringData*>(ar->getClass()->name())); } Variant intArgs = PackedArrayInit(5) .append(VarNR(ar->func()->fullName())) .append(called_on) .append(get_frame_args_with_ref(ar)) .append(h->asCArrRef()[1]) .appendRef(doneFlag) .toArray(); Variant ret = vm_call_user_func(h->asCArrRef()[0], intArgs); if (doneFlag.toBoolean()) { Offset pcOff; ActRec* outer = g_context->getPrevVMState(ar, &pcOff); frame_free_locals_inl_no_hook<true>(ar, ar->func()->numLocals()); Stack& stack = vmStack(); stack.top() = (Cell*)(ar + 1); cellDup(*ret.asCell(), *stack.allocTV()); vmfp() = outer; vmpc() = outer ? outer->func()->unit()->at(pcOff) : nullptr; return false; } vmfp() = ar; vmpc() = savePc; return true; }
// foldConst() is callable from the parse phase as well as the analysis phase. // We take advantage of this during the parse phase to reduce very simple // expressions down to a single scalar and keep the parse tree smaller, // especially in cases of long chains of binary operators. However, we limit // the effectivness of this during parse to ensure that we eliminate only // very simple scalars that don't require analysis in later phases. For now, // that's just simply scalar values. ExpressionPtr BinaryOpExpression::foldConst(AnalysisResultConstPtr ar) { ExpressionPtr optExp; Variant v1; Variant v2; if (!m_exp2->getScalarValue(v2)) { if ((ar->getPhase() != AnalysisResult::ParseAllFiles) && m_exp1->isScalar() && m_exp1->getScalarValue(v1)) { switch (m_op) { case T_IS_IDENTICAL: case T_IS_NOT_IDENTICAL: if (v1.isNull()) { return makeIsNull(ar, getLocation(), m_exp2, m_op == T_IS_NOT_IDENTICAL); } break; case T_LOGICAL_AND: case T_BOOLEAN_AND: case T_LOGICAL_OR: case T_BOOLEAN_OR: { ExpressionPtr rep = v1.toBoolean() == (m_op == T_LOGICAL_AND || m_op == T_BOOLEAN_AND) ? m_exp2 : m_exp1; rep = ExpressionPtr( new UnaryOpExpression( getScope(), getLocation(), rep, T_BOOL_CAST, true)); rep->setActualType(Type::Boolean); return replaceValue(rep); } case '+': case '.': case '*': case '&': case '|': case '^': if (m_exp2->is(KindOfBinaryOpExpression)) { BinaryOpExpressionPtr binOpExp = dynamic_pointer_cast<BinaryOpExpression>(m_exp2); if (binOpExp->m_op == m_op && binOpExp->m_exp1->isScalar()) { ExpressionPtr aExp = m_exp1; ExpressionPtr bExp = binOpExp->m_exp1; ExpressionPtr cExp = binOpExp->m_exp2; if (aExp->isArray() || bExp->isArray() || cExp->isArray()) { break; } m_exp1 = binOpExp = Clone(binOpExp); m_exp2 = cExp; binOpExp->m_exp1 = aExp; binOpExp->m_exp2 = bExp; if (ExpressionPtr optExp = binOpExp->foldConst(ar)) { m_exp1 = optExp; } return static_pointer_cast<Expression>(shared_from_this()); } } break; default: break; } } return ExpressionPtr(); } if (m_exp1->isScalar()) { if (!m_exp1->getScalarValue(v1)) return ExpressionPtr(); try { ScalarExpressionPtr scalar1 = dynamic_pointer_cast<ScalarExpression>(m_exp1); ScalarExpressionPtr scalar2 = dynamic_pointer_cast<ScalarExpression>(m_exp2); // Some data, like the values of __CLASS__ and friends, are not available // while we're still in the initial parse phase. if (ar->getPhase() == AnalysisResult::ParseAllFiles) { if ((scalar1 && scalar1->needsTranslation()) || (scalar2 && scalar2->needsTranslation())) { return ExpressionPtr(); } } if (!Option::WholeProgram || !Option::ParseTimeOpts) { // In the VM, don't optimize __CLASS__ if within a trait, since // __CLASS__ is not resolved yet. ClassScopeRawPtr clsScope = getOriginalClass(); if (clsScope && clsScope->isTrait()) { if ((scalar1 && scalar1->getType() == T_CLASS_C) || (scalar2 && scalar2->getType() == T_CLASS_C)) { return ExpressionPtr(); } } } Variant result; switch (m_op) { case T_LOGICAL_XOR: result = static_cast<bool>(v1.toBoolean() ^ v2.toBoolean()); break; case '|': *result.asCell() = cellBitOr(*v1.asCell(), *v2.asCell()); break; case '&': *result.asCell() = cellBitAnd(*v1.asCell(), *v2.asCell()); break; case '^': *result.asCell() = cellBitXor(*v1.asCell(), *v2.asCell()); break; case '.': if (v1.isArray() || v2.isArray()) { return ExpressionPtr(); } result = concat(v1.toString(), v2.toString()); break; case T_IS_IDENTICAL: result = same(v1, v2); break; case T_IS_NOT_IDENTICAL: result = !same(v1, v2); break; case T_IS_EQUAL: result = equal(v1, v2); break; case T_IS_NOT_EQUAL: result = !equal(v1, v2); break; case '<': result = less(v1, v2); break; case T_IS_SMALLER_OR_EQUAL: result = cellLessOrEqual(*v1.asCell(), *v2.asCell()); break; case '>': result = more(v1, v2); break; case T_IS_GREATER_OR_EQUAL: result = cellGreaterOrEqual(*v1.asCell(), *v2.asCell()); break; case '+': *result.asCell() = cellAdd(*v1.asCell(), *v2.asCell()); break; case '-': *result.asCell() = cellSub(*v1.asCell(), *v2.asCell()); break; case '*': *result.asCell() = cellMul(*v1.asCell(), *v2.asCell()); break; case '/': if ((v2.isIntVal() && v2.toInt64() == 0) || v2.toDouble() == 0.0) { return ExpressionPtr(); } *result.asCell() = cellDiv(*v1.asCell(), *v2.asCell()); break; case '%': if ((v2.isIntVal() && v2.toInt64() == 0) || v2.toDouble() == 0.0) { return ExpressionPtr(); } *result.asCell() = cellMod(*v1.asCell(), *v2.asCell()); break; case T_SL: result = v1.toInt64() << v2.toInt64(); break; case T_SR: result = v1.toInt64() >> v2.toInt64(); break; case T_BOOLEAN_OR: result = v1.toBoolean() || v2.toBoolean(); break; case T_BOOLEAN_AND: result = v1.toBoolean() && v2.toBoolean(); break; case T_LOGICAL_OR: result = v1.toBoolean() || v2.toBoolean(); break; case T_LOGICAL_AND: result = v1.toBoolean() && v2.toBoolean(); break; case T_INSTANCEOF: { if (v2.isString()) { if (v1.isArray() && interface_supports_array(v2.getStringData())) { result = true; break; } if (v1.isString() && interface_supports_string(v2.getStringData())) { result = true; break; } if (v1.isInteger() && interface_supports_int(v2.getStringData())) { result = true; break; } if (v1.isDouble() && interface_supports_double(v2.getStringData())) { result = true; break; } } result = false; break; } default: return ExpressionPtr(); } return makeScalarExpression(ar, result); } catch (...) { } } else if (ar->getPhase() != AnalysisResult::ParseAllFiles) {
bool f_is_callable(const Variant& v, bool syntax /* = false */, VRefParam name /* = null */) { bool ret = true; if (LIKELY(!syntax)) { CallerFrame cf; ObjectData* obj = NULL; HPHP::Class* cls = NULL; StringData* invName = NULL; const HPHP::Func* f = vm_decode_function(v, cf(), false, obj, cls, invName, false); if (f == NULL) { ret = false; } if (invName != NULL) { decRefStr(invName); } if (!name.isReferenced()) return ret; } auto const tv_func = v.asCell(); if (IS_STRING_TYPE(tv_func->m_type)) { if (name.isReferenced()) name = tv_func->m_data.pstr; return ret; } if (tv_func->m_type == KindOfArray) { const Array& arr = Array(tv_func->m_data.parr); const Variant& clsname = arr.rvalAtRef(int64_t(0)); const Variant& mthname = arr.rvalAtRef(int64_t(1)); if (arr.size() != 2 || &clsname == &null_variant || &mthname == &null_variant) { name = String("Array"); return false; } auto const tv_meth = mthname.asCell(); if (!IS_STRING_TYPE(tv_meth->m_type)) { if (name.isReferenced()) name = v.toString(); return false; } auto const tv_cls = clsname.asCell(); if (tv_cls->m_type == KindOfObject) { name = tv_cls->m_data.pobj->o_getClassName(); } else if (IS_STRING_TYPE(tv_cls->m_type)) { name = tv_cls->m_data.pstr; } else { name = v.toString(); return false; } name = concat3(name, s_colon2, tv_meth->m_data.pstr); return ret; } if (tv_func->m_type == KindOfObject) { ObjectData *d = tv_func->m_data.pobj; const Func* invoke = d->getVMClass()->lookupMethod(s__invoke.get()); if (name.isReferenced()) { if (d->instanceof(c_Closure::classof())) { // Hack to stop the mangled name from showing up name = s_Closure__invoke; } else { name = d->o_getClassName() + "::__invoke"; } } return invoke != NULL; } return false; }