Call zend_startup_strtod so ezc extensions can use zend_strtod.
[hiphop-php.git] / hphp / hhbbc / interp-minstr.cpp
blob2a5bcbcd735f34fb95e4c65c1d544a82ce2aa181
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2014 Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
16 #include "hphp/hhbbc/interp.h"
18 #include <vector>
19 #include <algorithm>
20 #include <string>
21 #include <utility>
23 #include <folly/Optional.h>
24 #include <folly/Format.h>
26 #include "hphp/util/trace.h"
28 #include "hphp/hhbbc/interp-internal.h"
29 #include "hphp/hhbbc/type-ops.h"
31 namespace HPHP { namespace HHBBC {
33 namespace {
35 //////////////////////////////////////////////////////////////////////
37 const StaticString s_stdClass("stdClass");
39 //////////////////////////////////////////////////////////////////////
42 * Note: the couldBe comparisons here with sempty() are asking "can this string
43 * be a non-reference counted empty string". What actually matters is whether
44 * it can be an empty string at all. Currently, all reference counted strings
45 * are TStr, which has no values and may also be non-reference
46 * counted---emptiness isn't separately tracked like it is for arrays, so if
47 * anything happened that could make it reference counted this check will
48 * return true.
50 * This means this code is fine for now, but if we implement #3837503
51 * (non-static strings with values in the type system) it will need to change.
54 bool couldBeEmptyish(Type ty) {
55 return ty.couldBe(TNull) ||
56 ty.couldBe(sempty()) ||
57 ty.couldBe(TFalse);
60 bool mustBeEmptyish(Type ty) {
61 return ty.subtypeOf(TNull) ||
62 ty.subtypeOf(sempty()) ||
63 ty.subtypeOf(TFalse);
66 bool elemCouldPromoteToArr(Type ty) { return couldBeEmptyish(ty); }
67 bool propCouldPromoteToObj(Type ty) { return couldBeEmptyish(ty); }
68 bool elemMustPromoteToArr(Type ty) { return mustBeEmptyish(ty); }
69 bool propMustPromoteToObj(Type ty) { return mustBeEmptyish(ty); }
71 //////////////////////////////////////////////////////////////////////
74 * Tag indicating what sort of thing contains the current base.
76 * The base is always the unboxed version of the type, and its
77 * location could be inside of a Ref. So, for example, a base with
78 * BaseLoc::Frame could be located inside of a Ref that is pointed
79 * to by the Frame. (We may want to distinguish these two cases at
80 * some point if we start trying to track real information about
81 * Refs, but not yet.)
83 enum class BaseLoc {
85 * Base is in a number of possible places after an Elem op. It
86 * cannot possibly be in an object property (although it certainly
87 * may alias one). See miElem for details. Not all post-elem ops
88 * use this location (see LocalArrChain).
90 * If it is definitely in an array, the locTy in the Base will be
91 * a subtype of TArr.
93 PostElem,
96 * Base is in possible locations after a Prop op. This means it
97 * possibly lives in a property on an object, but possibly not
98 * (e.g. it could be a null in tvScratch). See miProp for
99 * details.
101 * If it is definitely known to be a property in an object, the
102 * locTy in the Base will be a subtype of TObj.
104 PostProp,
107 * Known to be a static property on an object. This is only
108 * possible as an initial base.
110 StaticObjProp,
113 * The base is inside of a local that contains a specialized array
114 * type, and the arrayChain is non-empty.
116 * When the location is set to this, the chain will continue as
117 * long as we keep staying inside specialized array types. If it
118 * moves to something like a ?Arr type, we must leave the chain
119 * when the base moves.
121 LocalArrChain,
124 * Known to be contained in the current frame as a local, as the
125 * frame $this, by the evaluation stack, or inside $GLOBALS. Only
126 * possible as initial bases.
128 Frame,
129 FrameThis,
130 EvalStack,
131 Global,
134 * If we've execute an operation that's known to fatal, we use
135 * this BaseLoc.
137 Fataled,
140 struct Base {
141 Type type;
142 BaseLoc loc;
145 * We also need to track effects of intermediate dims on the type
146 * of the base. So we have a type, name, and possibly associated
147 * local for the base's container.
149 * For StaticObjProp, locName this is the name of the property if
150 * known, or nullptr, and locTy is the type of the class
151 * containing the static property.
153 * Similarly, if loc is PostProp, locName is the name of the
154 * property if it was known, and locTy gives as much information
155 * about the object type it is in. (If we actually *know* it is
156 * in an object, locTy will be a subtype of TObj.)
158 Type locTy;
159 SString locName;
160 borrowed_ptr<php::Local> local;
163 std::string base_string(const Base& b) {
164 auto const locStr = [&]() -> const char* {
165 switch (b.loc) {
166 case BaseLoc::PostElem: return "PostElem";
167 case BaseLoc::PostProp: return "PostProp";
168 case BaseLoc::StaticObjProp: return "StaticObjProp";
169 case BaseLoc::LocalArrChain: return "ArrChain";
170 case BaseLoc::Frame: return "Frame";
171 case BaseLoc::FrameThis: return "FrameThis";
172 case BaseLoc::EvalStack: return "EvalStack";
173 case BaseLoc::Global: return "Global";
174 case BaseLoc::Fataled: return "Fataled";
176 not_reached();
177 }();
178 return folly::format(
179 "{: <32} ({: <14} {: <8} @ {})",
180 show(b.type),
181 locStr,
182 show(b.locTy),
183 b.locName ? b.locName->data() : "?"
184 ).str();
187 Type baseLocNameType(const Base& b) {
188 return b.locName ? sval(b.locName) : TInitGen;
191 struct MIS : ISS {
192 explicit MIS(ISS& env,
193 const MInstrInfo* info,
194 const MVector& mvec)
195 : ISS(env)
196 , info(*info)
197 , mvec(mvec)
198 , stackIdx(info->valCount() + numVecPops(mvec))
202 * Return the current MElem. Only valid after the first base has
203 * been processed.
205 const MElem& melem() const {
206 assert(mInd < mvec.mcodes.size());
207 return mvec.mcodes[mInd];
211 * Return the current MemberCode. Only valid after the first base
212 * has been processed.
214 MemberCode mcode() const { return melem().mcode; }
216 const MInstrInfo& info;
217 const MVector& mvec;
220 * The current base. Updated as we move through the vector
221 * instruction.
223 Base base;
226 * Chains of operations on array elements will all affect the type
227 * of something further back in the member instruction. Currently
228 * this is just used for locals. This vector tracks the base,key
229 * type pair that was used at each stage. See resolveArrayChain.
231 std::vector<std::pair<Type,Type>> arrayChain;
234 * Current index in mcodes vector (or 0 if we still haven't done
235 * the base).
237 uint32_t mInd = 0;
240 * One above next stack slot to read, going forward in the mvec
241 * (deeper to higher on stack).
243 int32_t stackIdx;
246 //////////////////////////////////////////////////////////////////////
249 * A note about bases.
251 * Generally type inference needs to know two kinds of things about
252 * the base to handle effects on tracked locations:
254 * - Could the base be a location we're tracking deeper structure
255 * on, so the next operation actually affects something inside
256 * of it. For example, could the base be an object with the
257 * same type as $this, or an array in a local variable.
259 * - Could the base be something (regardless of type) that is
260 * inside one of the things we're tracking. I.e., the base
261 * might be whatever (an array or a bool or something), but
262 * living inside a property inside an object with the same type
263 * as $this, or living inside of an array in the local frame.
265 * The first cases apply because final operations are going to
266 * directly affect the type of these elements. The second case is
267 * because vector operations may change the base at each step if it
268 * is a defining instruction.
270 * Note that both of these cases can apply to the same base in some
271 * cases: you might have an object property on $this that could be
272 * an object of the type of $this.
274 * The functions below with names "couldBeIn*" detect the second
275 * case. The effects on the tracked location in the second case are
276 * handled in the functions with names "handleIn*{Prop,Elem,..}".
277 * The effects for the first case are generally handled in the
278 * miFinal op functions.
280 * Control flow insensitive vs. control flow sensitive types:
282 * Things are also slightly complicated by the fact that we are
283 * analyzing some control flow insensitve types along side precisely
284 * tracked types. For effects on locals, we perform the type
285 * effects of each operation on base.type, and then allow moveBase()
286 * to make the updates to the local when we know what its final type
287 * will be.
289 * This approach doesn't do as well for possible properties in $this
290 * or self::, because we may see situations where the base could be
291 * one of these properties but we're not sure---perhaps because it
292 * came off a property with the same name on an object with an
293 * unknown type (i.e. base.type is InitCell but couldBeInThis is
294 * true). In these situations, we can get away with just merging
295 * Obj=stdClass into the thisProp (because it 'could' promote)
296 * instead of merging the whole InitCell, which possibly lets us
297 * leave the type at ?Obj in some cases.
299 * This is why there's two fairly different mechanisms for handling
300 * the effects of defining ops on base types.
303 //////////////////////////////////////////////////////////////////////
305 bool couldBeThisObj(ISS& env, const Base& b) {
306 if (b.loc == BaseLoc::Fataled) return false;
307 auto const thisTy = thisType(env);
308 return b.type.couldBe(thisTy ? *thisTy : TObj);
311 bool mustBeThisObj(ISS& env, const Base& b) {
312 if (b.loc == BaseLoc::FrameThis) return true;
313 if (auto const ty = thisType(env)) return b.type.subtypeOf(*ty);
314 return false;
317 bool mustBeInFrame(const Base& b) {
318 return b.loc == BaseLoc::Frame;
321 bool couldBeInThis(ISS& env, const Base& b) {
322 if (b.loc != BaseLoc::PostProp) return false;
323 auto const thisTy = thisType(env);
324 if (!thisTy) return true;
325 if (!b.locTy.couldBe(*thisTy)) return false;
326 if (b.locName) {
327 return isTrackedThisProp(env, b.locName);
329 return true;
332 bool couldBeInSelf(ISS& env, const Base& b) {
333 if (b.loc != BaseLoc::StaticObjProp) return false;
334 auto const selfTy = selfCls(env);
335 return !selfTy || b.locTy.couldBe(*selfTy);
338 bool couldBeInPublicStatic(ISS& env, const Base& b) {
339 return b.loc == BaseLoc::StaticObjProp;
342 //////////////////////////////////////////////////////////////////////
344 void handleInThisPropD(MIS& env) {
345 // NullSafe (Q) props do not promote an emptyish base to stdClass instance.
346 if (env.mvec.mcodes[env.mInd].mcode == MQT) {
347 return;
349 if (!couldBeInThis(env, env.base)) return;
351 if (auto const name = env.base.locName) {
352 auto const ty = thisPropAsCell(env, name);
353 if (ty && propCouldPromoteToObj(*ty)) {
354 mergeThisProp(env, name,
355 objExact(env.index.builtin_class(s_stdClass.get())));
357 return;
360 mergeEachThisPropRaw(env, [&] (Type t) {
361 return propCouldPromoteToObj(t) ? TObj : TBottom;
365 void handleInSelfPropD(MIS& env) {
366 // NullSafe (Q) props do not promote an emptyish base to stdClass instance.
367 if (env.mvec.mcodes[env.mInd].mcode == MQT) {
368 return;
371 if (!couldBeInSelf(env, env.base)) return;
373 if (auto const name = env.base.locName) {
374 auto const ty = selfPropAsCell(env, name);
375 if (ty && propCouldPromoteToObj(*ty)) {
376 mergeSelfProp(env, name,
377 objExact(env.index.builtin_class(s_stdClass.get())));
379 return;
382 loseNonRefSelfPropTypes(env);
385 void handleInPublicStaticPropD(MIS& env) {
386 // NullSafe (Q) props do not promote an emptyish base to stdClass instance.
387 if (env.mvec.mcodes[env.mInd].mcode == MQT) {
388 return;
391 if (!couldBeInPublicStatic(env, env.base)) return;
393 auto const indexer = env.collect.publicStatics;
394 if (!indexer) return;
396 auto const name = baseLocNameType(env.base);
397 auto const ty = env.index.lookup_public_static(env.base.locTy, name);
398 if (propCouldPromoteToObj(ty)) {
399 indexer->merge(env.ctx, env.base.locTy, name,
400 objExact(env.index.builtin_class(s_stdClass.get())));
404 void handleInThisElemD(MIS& env) {
405 if (!couldBeInThis(env, env.base)) return;
407 if (auto const name = env.base.locName) {
408 auto const ty = thisPropAsCell(env, name);
409 if (ty && elemCouldPromoteToArr(*ty)) {
410 mergeThisProp(env, name, TArr);
412 return;
415 mergeEachThisPropRaw(env, [&] (Type t) {
416 return elemCouldPromoteToArr(t) ? TArr : TBottom;
420 void handleInSelfElemD(MIS& env) {
421 if (!couldBeInSelf(env, env.base)) return;
423 if (auto const name = env.base.locName) {
424 if (auto const ty = selfPropAsCell(env, name)) {
425 if (elemCouldPromoteToArr(*ty)) {
426 mergeSelfProp(env, name, TArr);
428 mergeSelfProp(env, name, loosen_statics(*ty));
430 return;
432 loseNonRefSelfPropTypes(env);
435 void handleInPublicStaticElemD(MIS& env) {
436 if (!couldBeInPublicStatic(env, env.base)) return;
438 auto const indexer = env.collect.publicStatics;
439 if (!indexer) return;
441 auto const name = baseLocNameType(env.base);
442 auto const ty = env.index.lookup_public_static(env.base.locTy, name);
443 if (elemCouldPromoteToArr(ty)) {
444 // Might be possible to only merge a TArrE, but for now this is ok.
445 indexer->merge(env.ctx, env.base.locTy, name, TArr);
449 // Currently NewElem and Elem InFoo effects don't need to do
450 // anything different from each other.
451 void handleInThisNewElem(MIS& env) { handleInThisElemD(env); }
452 void handleInSelfNewElem(MIS& env) { handleInSelfElemD(env); }
453 void handleInPublicStaticNewElem(MIS& env) { handleInPublicStaticElemD(env); }
455 void handleInSelfElemU(MIS& env) {
456 if (!couldBeInSelf(env, env.base)) return;
458 if (auto const name = env.base.locName) {
459 auto const ty = selfPropAsCell(env, name);
460 if (ty) mergeSelfProp(env, name, loosen_statics(*ty));
461 } else {
462 mergeEachSelfPropRaw(env, loosen_statics);
466 void handleInPublicStaticElemU(MIS& env) {
467 if (!couldBeInPublicStatic(env, env.base)) return;
469 auto const indexer = env.collect.publicStatics;
470 if (!indexer) return;
473 * We need to ensure that the type could become non-static, but since we're
474 * never going to see anything specialized from lookup_public_static the
475 * first time we're running with collect.publicStatics, we can't do much
476 * right now since we don't have a type for the union of all counted types.
478 * Merging InitCell is correct, but very conservative, for now.
480 auto const name = baseLocNameType(env.base);
481 indexer->merge(env.ctx, env.base.locTy, name, TInitCell);
484 //////////////////////////////////////////////////////////////////////
486 // MInstrs can throw in between each op, so the states of locals
487 // need to be propagated across factored exit edges.
488 void miThrow(MIS& env) {
489 for (auto& factored : env.blk.factoredExits) {
490 env.propagate(*factored, without_stacks(env.state));
494 //////////////////////////////////////////////////////////////////////
496 void setLocalForBase(MIS& env, Type ty) {
497 assert(mustBeInFrame(env.base) ||
498 env.base.loc == BaseLoc::LocalArrChain);
499 if (!env.base.local) return loseNonRefLocalTypes(env);
500 setLoc(env, env.base.local, ty);
501 FTRACE(4, " ${} := {}\n",
502 env.base.locName ? env.base.locName->data() : "$<unnamed>",
503 show(ty)
507 // Run backwards through an array chain doing array_set operations
508 // to produce the array type that incorporates the effects of any
509 // intermediate defining dims.
510 Type currentChainType(MIS& env, Type val) {
511 auto it = env.arrayChain.rbegin();
512 for (; it != env.arrayChain.rend(); ++it) {
513 val = array_set(it->first, it->second, val);
515 return val;
518 Type resolveArrayChain(MIS& env, Type val) {
519 static UNUSED const char prefix[] = " ";
520 FTRACE(5, "{}chain\n", prefix, show(val));
521 do {
522 auto arr = std::move(env.arrayChain.back().first);
523 auto key = std::move(env.arrayChain.back().second);
524 assert(arr.subtypeOf(TArr));
525 env.arrayChain.pop_back();
526 FTRACE(5, "{} | {} := {} in {}\n", prefix,
527 show(key), show(val), show(arr));
528 val = array_set(std::move(arr), key, val);
529 } while (!env.arrayChain.empty());
530 FTRACE(5, "{} = {}\n", prefix, show(val));
531 return val;
534 void moveBase(MIS& env, folly::Optional<Base> base) {
535 SCOPE_EXIT { if (base) env.base = *base; };
537 // Note: these miThrows probably can be left out if base is
538 // folly::none (i.e. we're on the last dim).
540 auto const& ty = env.base.type;
541 if (mustBeInFrame(env.base)) {
542 setLocalForBase(env, ty);
543 miThrow(env);
544 return;
547 if (!env.arrayChain.empty()) {
548 auto const continueChain = base && base->loc == BaseLoc::LocalArrChain;
549 if (!continueChain) {
550 setLocalForBase(env, resolveArrayChain(env, env.base.type));
551 return;
555 * We have a chain in progress, but it's not done. But we still
556 * need to throw any type effects on the local across factored
557 * edges.
559 setLocalForBase(env, currentChainType(env, env.base.type));
560 miThrow(env);
564 //////////////////////////////////////////////////////////////////////
567 * The following handleBase{Elem,Prop}* functions are used to implement the
568 * 'normal' portion of the effects on base types, which are mostly what are
569 * done by intermediate dims. (Contrast with the handleInXXX{Elem,Prop}
570 * functions, which handle the effects on the type of the thing that's
571 * /containing/ the base.)
573 * The contract with these functions is that they should handle all the effects
574 * on the base type /except/ for the case of the base being a subtype of
575 * TArr---the caller is responsible for that. The reason for this is that for
576 * tracking effects on specialized array types (e.g. LocalArrChain), the final
577 * ops generally need to do completely different things to the array, so this
578 * allows reuse of this shared part of the type transitions. The
579 * miIntermediate routines must handle subtypes of TArr outside of calls to
580 * this as well.
583 void handleBaseElemU(MIS& env) {
584 auto& ty = env.base.type;
585 if (ty.couldBe(TArr)) {
586 // We're conservative with unsets on array types for now.
587 ty = union_of(ty, TArr);
589 if (ty.couldBe(TSStr)) {
590 ty = loosen_statics(env.base.type);
594 void handleBasePropD(MIS& env) {
595 // NullSafe (Q) props do not promote an emptyish base to stdClass instance.
596 if (env.mvec.mcodes[env.mInd].mcode == MQT) {
597 return;
599 auto& ty = env.base.type;
600 if (ty.subtypeOf(TObj)) return;
601 if (propMustPromoteToObj(ty)) {
602 ty = objExact(env.index.builtin_class(s_stdClass.get()));
603 return;
605 if (propCouldPromoteToObj(ty)) {
606 ty = promote_emptyish(ty, TObj);
607 return;
611 void handleBaseElemD(MIS& env) {
612 auto& ty = env.base.type;
614 // When the base is actually a subtype of array, we handle it in the callers
615 // of these functions.
616 if (ty.subtypeOf(TArr)) return;
618 if (elemMustPromoteToArr(ty)) {
619 ty = counted_aempty();
620 return;
622 // Intermediate ElemD operations on strings fatal, unless the
623 // string is empty, which promotes to array. So for any string
624 // here we can assume it promoted to an empty array.
625 if (ty.subtypeOf(TStr)) {
626 ty = counted_aempty();
627 return;
629 if (elemCouldPromoteToArr(ty)) {
630 ty = promote_emptyish(ty, counted_aempty());
634 * If the base still couldBe some kind of array (but isn't a subtype of TArr,
635 * which would be handled outside this routine), we need to give up on any
636 * information better than TArr here (or track the effects, but we're not
637 * doing that yet).
639 if (ty.couldBe(TArr)) {
640 ty = union_of(ty, TArr);
644 void handleBaseNewElem(MIS& env) {
645 handleBaseElemD(env);
646 // Technically we don't need to do TStr case.
649 //////////////////////////////////////////////////////////////////////
651 Type mcodeKey(MIS& env) {
652 auto const melem = env.mvec.mcodes[env.mInd];
653 switch (melem.mcode) {
654 case MPC: return topC(env, --env.stackIdx);
655 case MPL: return locAsCell(env, melem.immLoc);
656 case MPT: return sval(melem.immStr);
657 case MQT: return sval(melem.immStr);
658 case MEC: return topC(env, --env.stackIdx);
659 case MET: return sval(melem.immStr);
660 case MEL: return locAsCell(env, melem.immLoc);
661 case MEI: return ival(melem.immInt);
663 case MW:
664 case InvalidMemberCode:
665 always_assert(0);
666 break;
668 not_reached();
671 // Returns nullptr if it's an unknown key or not a string.
672 SString mcodeStringKey(MIS& env) {
673 auto const v = tv(mcodeKey(env));
674 return v && v->m_type == KindOfStaticString ? v->m_data.pstr : nullptr;
677 void miPop(MIS& env) {
678 auto& mvec = env.mvec;
679 if (mvec.lcode == LSL || mvec.lcode == LSC) {
680 assert(env.stackIdx == env.info.valCount() + 1 /* clsref */);
681 } else {
682 assert(env.stackIdx == env.info.valCount());
684 for (auto i = uint32_t{0}; i < numVecPops(mvec); ++i) popT(env);
687 //////////////////////////////////////////////////////////////////////
688 // base ops
690 Base miBaseLoc(MIS& env) {
691 bool const isDefine = env.info.getAttr(env.mvec.lcode) & MIA_define;
692 auto const locBase = env.mvec.locBase;
694 if (!isDefine) {
695 return Base { derefLoc(env, locBase),
696 BaseLoc::Frame,
697 TBottom,
698 locBase->name,
699 locBase };
702 // We're changing the local to define it, but we don't need to do
703 // an miThrow yet---the promotions (to array or stdClass) on
704 // previously uninitialized locals happen before raising warnings
705 // that could throw, so we can wait until the first moveBase.
706 ensureInit(env, locBase);
707 return Base { locAsCell(env, locBase),
708 BaseLoc::Frame,
709 TBottom,
710 locBase->name,
711 locBase };
714 Base miBaseSProp(MIS& env, Type cls, Type tprop) {
715 auto const self = selfCls(env);
716 auto const prop = tv(tprop);
717 auto const name = prop && prop->m_type == KindOfStaticString
718 ? prop->m_data.pstr : nullptr;
719 if (self && cls.subtypeOf(*self) && name) {
720 if (auto const ty = selfPropAsCell(env, prop->m_data.pstr)) {
721 return Base { *ty, BaseLoc::StaticObjProp, cls, name };
724 auto const indexTy = env.index.lookup_public_static(cls, tprop);
725 if (indexTy.subtypeOf(TInitCell)) {
726 return Base { indexTy, BaseLoc::StaticObjProp, cls, name };
728 return Base { TInitCell, BaseLoc::StaticObjProp, cls, name };
731 Base miBase(MIS& env) {
732 auto& mvec = env.mvec;
733 switch (mvec.lcode) {
734 case LL:
735 return miBaseLoc(env);
736 case LC:
737 return Base { topC(env, --env.stackIdx), BaseLoc::EvalStack };
738 case LR:
740 auto const t = topR(env, --env.stackIdx);
741 return Base { t.subtypeOf(TInitCell) ? t : TInitCell,
742 BaseLoc::EvalStack };
744 case LH:
746 auto const ty = thisType(env);
747 return Base { ty ? *ty : TObj, BaseLoc::FrameThis };
749 case LGL:
750 locAsCell(env, env.mvec.locBase);
751 return Base { TInitCell, BaseLoc::Global };
752 case LGC:
753 topC(env, --env.stackIdx);
754 return Base { TInitCell, BaseLoc::Global };
756 // The first moveBase() on these unknown local bases will cause
757 // a env.loseNonRefLocalTypes.
758 case LNL:
759 return Base { TInitCell, BaseLoc::Frame };
760 case LNC:
761 topC(env, --env.stackIdx);
762 return Base { TInitCell, BaseLoc::Frame };
764 case LSL:
766 auto const cls = topA(env, env.info.valCount());
767 auto const prop = locAsCell(env, env.mvec.locBase);
768 return miBaseSProp(env, cls, prop);
770 case LSC:
772 auto const cls = topA(env, env.info.valCount());
773 auto const prop = topC(env, --env.stackIdx);
774 return miBaseSProp(env, cls, prop);
777 case InvalidLocationCode:
778 break;
780 not_reached();
783 //////////////////////////////////////////////////////////////////////
784 // intermediate ops
786 void miProp(MIS& env) {
787 auto const name = mcodeStringKey(env);
788 auto const isDefine = env.info.getAttr(env.mcode()) & MIA_define;
789 auto const isUnset = env.info.getAttr(env.mcode()) & MIA_unset;
790 auto const isNullsafe = (env.mvec.mcodes[env.mInd].mcode == MQT);
793 * MIA_unset Props doesn't promote "emptyish" things to stdClass,
794 * or affect arrays, however it can define a property on an object
795 * base. This means we don't need any couldBeInFoo logic, but if
796 * the base could actually be $this, and a declared property could
797 * be Uninit, we need to merge InitNull.
799 * We're trying to handle this case correctly as far as the type
800 * inference here is concerned, but the runtime doesn't actually
801 * behave this way right now for declared properties. Note that
802 * it never hurts to merge more types than a thisProp could
803 * actually be, so this is fine.
805 * See TODO(#3602740): unset with intermediate dims on previously
806 * declared properties doesn't define them to null.
808 if (isUnset && couldBeThisObj(env, env.base)) {
809 if (name) {
810 auto const ty = thisPropRaw(env, name);
811 if (ty && ty->couldBe(TUninit)) {
812 mergeThisProp(env, name, TInitNull);
814 } else {
815 mergeEachThisPropRaw(env, [&] (Type ty) {
816 return ty.couldBe(TUninit) ? TInitNull : TBottom;
821 if (isDefine) {
822 handleInThisPropD(env);
823 handleInSelfPropD(env);
824 handleInPublicStaticPropD(env);
825 handleBasePropD(env);
828 if (mustBeThisObj(env, env.base)) {
829 auto const optThisTy = thisType(env);
830 auto const thisTy = optThisTy ? *optThisTy : TObj;
831 if (name) {
832 auto const propTy = thisPropAsCell(env, name);
833 moveBase(env, Base { propTy ? *propTy : TInitCell,
834 BaseLoc::PostProp,
835 thisTy,
836 name });
837 } else {
838 moveBase(env, Base { TInitCell, BaseLoc::PostProp, thisTy });
840 return;
843 // We know for sure we're going to be in an object property.
844 if (env.base.type.subtypeOf(TObj)) {
845 moveBase(env, Base { TInitCell,
846 BaseLoc::PostProp,
847 env.base.type,
848 name });
849 return;
853 * Otherwise, intermediate props with define can promote a null, false, or ""
854 * to stdClass. Those cases, and others, if it's not MIA_define, will set
855 * the base to a null value in tvScratch. The base may also legitimately be
856 * an object and our next base is in an object property.
858 * If we know for sure we're promoting to stdClass, we can put the locType
859 * pointing at that. Otherwise we conservatively treat all these cases as
860 * "possibly" being inside of an object property with "PostProp" with locType
861 * TTop.
863 auto const newBaseLocTy =
864 isDefine && !isNullsafe && propMustPromoteToObj(env.base.type)
865 ? objExact(env.index.builtin_class(s_stdClass.get()))
866 : TTop;
868 moveBase(env, Base { TInitCell, BaseLoc::PostProp, newBaseLocTy, name });
871 void miElem(MIS& env) {
872 auto const key = mcodeKey(env);
873 bool const isDefine = env.info.getAttr(env.mcode()) & MIA_define;
874 bool const isUnset = env.info.getAttr(env.mcode()) & MIA_unset;
877 * Elem dims with MIA_unset can change a base from a static array
878 * into a reference counted array. It never promotes emptyish
879 * types, however.
881 * We only need to handle this for self props, because we don't
882 * track static-ness on this props. The similar effect on local
883 * bases is handled in miBase.
885 if (isUnset) {
886 handleInSelfElemU(env);
887 handleInPublicStaticElemU(env);
888 handleBaseElemU(env);
891 if (isDefine) {
892 handleInThisElemD(env);
893 handleInSelfElemD(env);
894 handleInPublicStaticElemD(env);
895 handleBaseElemD(env);
897 auto const couldDoChain =
898 mustBeInFrame(env.base) || env.base.loc == BaseLoc::LocalArrChain;
899 if (couldDoChain && env.base.type.subtypeOf(TArr)) {
900 env.arrayChain.emplace_back(env.base.type, key);
901 moveBase(env, Base { array_elem(env.base.type, key),
902 BaseLoc::LocalArrChain,
903 TBottom,
904 env.base.locName,
905 env.base.local });
906 return;
910 if (env.base.type.subtypeOf(TArr)) {
911 moveBase(env, Base { array_elem(env.base.type, key),
912 BaseLoc::PostElem,
913 env.base.type });
914 return;
916 if (env.base.type.subtypeOf(TStr)) {
917 moveBase(env, Base { TStr, BaseLoc::PostElem });
918 return;
922 * Other cases could leave the base as anything (if nothing else,
923 * via ArrayAccess on an object).
925 * The resulting BaseLoc is either inside an array, is the global
926 * init_null_variant, or inside tvScratch. We represent this with
927 * the PostElem base location with locType TTop.
929 moveBase(env, Base { TInitCell, BaseLoc::PostElem, TTop });
932 void miNewElem(MIS& env) {
933 if (!env.info.newElem()) {
934 moveBase(env, Base { TInitCell, BaseLoc::Fataled });
935 return;
938 handleInThisNewElem(env);
939 handleInSelfNewElem(env);
940 handleInPublicStaticNewElem(env);
941 handleBaseNewElem(env);
943 auto const couldDoChain =
944 mustBeInFrame(env.base) || env.base.loc == BaseLoc::LocalArrChain;
945 if (couldDoChain && env.base.type.subtypeOf(TArr)) {
946 env.arrayChain.push_back(
947 array_newelem_key(env.base.type, TInitNull));
948 moveBase(env, Base { TInitNull,
949 BaseLoc::LocalArrChain,
950 TBottom,
951 env.base.locName,
952 env.base.local });
953 return;
956 if (env.base.type.subtypeOf(TArr)) {
957 moveBase(env, Base { TInitNull, BaseLoc::PostElem, env.base.type });
958 return;
961 moveBase(env, Base { TInitCell, BaseLoc::PostElem, TTop });
964 void miIntermediate(MIS& env) {
965 if (mcodeIsProp(env.mcode())) return miProp(env);
966 if (mcodeIsElem(env.mcode())) return miElem(env);
967 return miNewElem(env);
970 //////////////////////////////////////////////////////////////////////
971 // final prop ops
973 void miFinalIssetProp(MIS& env) {
974 auto const name = mcodeStringKey(env);
975 miPop(env);
976 if (name && mustBeThisObj(env, env.base)) {
977 if (auto const pt = thisPropAsCell(env, name)) {
978 if (pt->subtypeOf(TNull)) return push(env, TFalse);
979 if (!pt->couldBe(TNull)) return push(env, TTrue);
982 push(env, TBool);
985 void miFinalCGetProp(MIS& env) {
986 auto const name = mcodeStringKey(env);
987 miPop(env);
988 if (name && mustBeThisObj(env, env.base)) {
989 if (auto const t = thisPropAsCell(env, name)) {
990 return push(env, *t);
993 push(env, TInitCell);
996 void miFinalVGetProp(MIS& env) {
997 auto const name = mcodeStringKey(env);
998 miPop(env);
999 handleInThisPropD(env);
1000 handleInSelfPropD(env);
1001 handleInPublicStaticPropD(env);
1002 handleBasePropD(env);
1003 if (couldBeThisObj(env, env.base)) {
1004 if (name) {
1005 boxThisProp(env, name);
1006 } else {
1007 killThisProps(env);
1010 push(env, TRef);
1013 void miFinalSetProp(MIS& env) {
1014 auto const name = mcodeStringKey(env);
1015 auto const t1 = popC(env);
1017 miPop(env);
1018 handleInThisPropD(env);
1019 handleInSelfPropD(env);
1020 handleInPublicStaticPropD(env);
1021 handleBasePropD(env);
1023 auto const resultTy = env.base.type.subtypeOf(TObj) ? t1 : TInitCell;
1025 if (couldBeThisObj(env, env.base)) {
1026 if (!name) {
1027 mergeEachThisPropRaw(env, [&] (Type propTy) -> Type {
1028 if (propTy.couldBe(TInitCell)) {
1029 return union_of(std::move(propTy), t1);
1031 return TBottom;
1033 push(env, resultTy);
1034 return;
1036 mergeThisProp(env, name, t1);
1037 push(env, resultTy);
1038 return;
1041 push(env, resultTy);
1044 void miFinalSetOpProp(MIS& env, SetOpOp subop) {
1045 auto const name = mcodeStringKey(env);
1046 auto const rhsTy = popC(env);
1048 miPop(env);
1049 handleInThisPropD(env);
1050 handleInSelfPropD(env);
1051 handleInPublicStaticPropD(env);
1052 handleBasePropD(env);
1054 auto resultTy = TInitCell;
1056 if (couldBeThisObj(env, env.base)) {
1057 if (name && mustBeThisObj(env, env.base)) {
1058 if (auto const lhsTy = thisPropAsCell(env, name)) {
1059 resultTy = typeSetOp(subop, *lhsTy, rhsTy);
1063 if (name) {
1064 mergeThisProp(env, name, resultTy);
1065 } else {
1066 loseNonRefThisPropTypes(env);
1070 push(env, resultTy);
1073 void miFinalIncDecProp(MIS& env) {
1074 auto const name = mcodeStringKey(env);
1075 miPop(env);
1076 handleInThisPropD(env);
1077 handleInSelfPropD(env);
1078 handleInPublicStaticPropD(env);
1079 handleBasePropD(env);
1080 if (couldBeThisObj(env, env.base)) {
1081 if (name) {
1082 mergeThisProp(env, name, TInitCell);
1083 } else {
1084 loseNonRefThisPropTypes(env);
1087 push(env, TInitCell);
1090 void miFinalBindProp(MIS& env) {
1091 auto const name = mcodeStringKey(env);
1092 popV(env);
1093 miPop(env);
1094 handleInThisPropD(env);
1095 handleInSelfPropD(env);
1096 handleInPublicStaticPropD(env);
1097 handleBasePropD(env);
1098 if (couldBeThisObj(env, env.base)) {
1099 if (name) {
1100 boxThisProp(env, name);
1101 } else {
1102 killThisProps(env);
1105 push(env, TRef);
1108 void miFinalUnsetProp(MIS& env) {
1109 auto const name = mcodeStringKey(env);
1110 miPop(env);
1113 * Unset does define intermediate dims but with slightly different
1114 * rules than sets. It only applies to object properties.
1116 * Note that this can't affect self props, because static
1117 * properties can never be unset. It also can't change anything
1118 * about an inner array type.
1120 handleInThisPropD(env);
1122 if (couldBeThisObj(env, env.base)) {
1123 if (name) {
1124 unsetThisProp(env, name);
1125 } else {
1126 unsetUnknownThisProp(env);
1131 //////////////////////////////////////////////////////////////////////
1132 // Final elem ops
1134 // This is a helper for final defining Elem operations that need to
1135 // handle array chains and frame effects, but don't yet do anything
1136 // better than supplying a single type.
1137 void pessimisticFinalElemD(MIS& env, Type key, Type ty) {
1138 if (mustBeInFrame(env.base) && env.base.type.subtypeOf(TArr)) {
1139 env.base.type = array_set(env.base.type, key, ty);
1140 return;
1142 if (env.base.loc == BaseLoc::LocalArrChain) {
1143 if (env.base.type.subtypeOf(TArr)) {
1144 env.arrayChain.emplace_back(env.base.type, key);
1145 env.base.type = ty;
1150 void miFinalCGetElem(MIS& env) {
1151 auto const key = mcodeKey(env);
1152 auto const ty =
1153 env.base.type.subtypeOf(TArr)
1154 ? array_elem(env.base.type, key)
1155 : TInitCell;
1156 miPop(env);
1157 push(env, ty);
1160 void miFinalVGetElem(MIS& env) {
1161 auto const key = mcodeKey(env);
1162 miPop(env);
1163 handleInThisElemD(env);
1164 handleInSelfElemD(env);
1165 handleInPublicStaticElemD(env);
1166 handleBaseElemD(env);
1167 pessimisticFinalElemD(env, key, TInitGen);
1168 push(env, TRef);
1171 void miFinalSetElem(MIS& env) {
1172 auto const key = mcodeKey(env);
1173 auto const t1 = popC(env);
1174 miPop(env);
1176 handleInThisElemD(env);
1177 handleInSelfElemD(env);
1178 handleInPublicStaticElemD(env);
1180 // Note: we must handle the string-related cases before doing the
1181 // general handleBaseElemD, since operates on strings as if this
1182 // was an intermediate ElemD.
1183 if (env.base.type.subtypeOf(sempty())) {
1184 env.base.type = counted_aempty();
1185 } else {
1186 auto& ty = env.base.type;
1187 if (ty.couldBe(TStr)) {
1188 // Note here that a string type stays a string (with a changed character,
1189 // and loss of staticness), unless it was the empty string, where it
1190 // becomes an array. Do it conservatively for now:
1191 ty = union_of(loosen_statics(ty), counted_aempty());
1193 if (!ty.subtypeOf(TStr)) {
1194 handleBaseElemD(env);
1199 * In some unusual cases with illegal keys, SetM pushes null
1200 * instead of the right hand side.
1202 * There are also some special cases for SetM for different base types:
1203 * 1. If the base is a string, SetM pushes a new string with the
1204 * value of the first character of the right hand side converted
1205 * to a string (or something like that).
1206 * 2. If the base is a primitive type, SetM pushes null.
1207 * 3. If the base is an object, and it does not implement ArrayAccess,
1208 * it is still ok to push the right hand side, because it is a
1209 * fatal.
1211 * We push the right hand side on the stack only if the base is an
1212 * array, object or emptyish.
1214 auto const isWeird = key.couldBe(TObj) ||
1215 key.couldBe(TArr) ||
1216 (!env.base.type.subtypeOf(TArr) &&
1217 !env.base.type.subtypeOf(TObj) &&
1218 !mustBeEmptyish(env.base.type));
1220 if (mustBeInFrame(env.base) && env.base.type.subtypeOf(TArr)) {
1221 env.base.type = array_set(env.base.type, key, t1);
1222 push(env, isWeird ? TInitCell : t1);
1223 return;
1225 if (env.base.loc == BaseLoc::LocalArrChain) {
1226 if (env.base.type.subtypeOf(TArr)) {
1227 env.arrayChain.emplace_back(env.base.type, key);
1228 env.base.type = t1;
1229 push(env, isWeird ? TInitCell : t1);
1230 return;
1234 // ArrayAccess on $this will always push the rhs, even if things
1235 // were weird.
1236 if (mustBeThisObj(env, env.base)) return push(env, t1);
1238 push(env, isWeird ? TInitCell : t1);
1241 void miFinalSetOpElem(MIS& env) {
1242 auto const key = mcodeKey(env);
1243 popC(env);
1244 miPop(env);
1245 handleInThisElemD(env);
1246 handleInSelfElemD(env);
1247 handleInPublicStaticElemD(env);
1248 handleBaseElemD(env);
1249 pessimisticFinalElemD(env, key, TInitCell);
1250 push(env, TInitCell);
1253 void miFinalIncDecElem(MIS& env) {
1254 auto const key = mcodeKey(env);
1255 miPop(env);
1256 handleInThisElemD(env);
1257 handleInSelfElemD(env);
1258 handleInPublicStaticElemD(env);
1259 handleBaseElemD(env);
1260 pessimisticFinalElemD(env, key, TInitCell);
1261 push(env, TInitCell);
1264 void miFinalBindElem(MIS& env) {
1265 auto const key = mcodeKey(env);
1266 popV(env);
1267 miPop(env);
1268 handleInThisElemD(env);
1269 handleInSelfElemD(env);
1270 handleInPublicStaticElemD(env);
1271 handleBaseElemD(env);
1272 pessimisticFinalElemD(env, key, TInitGen);
1273 push(env, TRef);
1276 void miFinalUnsetElem(MIS& env) {
1277 mcodeKey(env);
1278 miPop(env);
1279 handleInSelfElemU(env);
1280 handleInPublicStaticElemU(env);
1281 handleBaseElemU(env);
1282 // We don't handle inner-array types with unset yet.
1283 always_assert(env.base.loc != BaseLoc::LocalArrChain);
1284 if (mustBeInFrame(env.base)) {
1285 always_assert(!env.base.type.strictSubtypeOf(TArr));
1289 //////////////////////////////////////////////////////////////////////
1290 // Final new elem ops
1292 // This is a helper for final defining Elem operations that need to
1293 // handle array chains and frame effects, but don't yet do anything
1294 // better than supplying a single type.
1295 void pessimisticFinalNewElem(MIS& env, Type ty) {
1296 if (mustBeInFrame(env.base) && env.base.type.subtypeOf(TArr)) {
1297 env.base.type = array_newelem(env.base.type, ty);
1298 return;
1300 if (env.base.loc == BaseLoc::LocalArrChain &&
1301 env.base.type.subtypeOf(TArr)) {
1302 env.base.type = array_newelem(env.base.type, ty);
1303 return;
1307 void miFinalVGetNewElem(MIS& env) {
1308 miPop(env);
1309 handleInThisNewElem(env);
1310 handleInSelfNewElem(env);
1311 handleInPublicStaticNewElem(env);
1312 handleBaseNewElem(env);
1313 pessimisticFinalNewElem(env, TInitGen);
1314 push(env, TRef);
1317 void miFinalSetNewElem(MIS& env) {
1318 auto const t1 = popC(env);
1319 miPop(env);
1320 handleInThisNewElem(env);
1321 handleInSelfNewElem(env);
1322 handleInPublicStaticNewElem(env);
1323 handleBaseNewElem(env);
1325 if (mustBeInFrame(env.base) && env.base.type.subtypeOf(TArr)) {
1326 env.base.type = array_newelem(env.base.type, t1);
1327 push(env, t1);
1328 return;
1330 if (env.base.loc == BaseLoc::LocalArrChain &&
1331 env.base.type.subtypeOf(TArr)) {
1332 env.base.type = array_newelem(env.base.type, t1);
1333 push(env, t1);
1334 return;
1337 // ArrayAccess on $this will always push the rhs.
1338 if (mustBeThisObj(env, env.base)) return push(env, t1);
1340 // TODO(#3343813): we should push the type of the rhs when we can;
1341 // SetM for a new elem still has some weird cases where it pushes
1342 // null instead to handle. (E.g. if the base is a number.)
1343 push(env, TInitCell);
1346 void miFinalSetOpNewElem(MIS& env) {
1347 popC(env);
1348 miPop(env);
1349 handleInThisNewElem(env);
1350 handleInSelfNewElem(env);
1351 handleInPublicStaticNewElem(env);
1352 handleBaseNewElem(env);
1353 pessimisticFinalNewElem(env, TInitCell);
1354 push(env, TInitCell);
1357 void miFinalIncDecNewElem(MIS& env) {
1358 miPop(env);
1359 handleInThisNewElem(env);
1360 handleInSelfNewElem(env);
1361 handleInPublicStaticNewElem(env);
1362 handleBaseNewElem(env);
1363 pessimisticFinalNewElem(env, TInitCell);
1364 push(env, TInitCell);
1367 void miFinalBindNewElem(MIS& env) {
1368 popV(env);
1369 miPop(env);
1370 handleInThisNewElem(env);
1371 handleInSelfNewElem(env);
1372 handleInPublicStaticNewElem(env);
1373 handleBaseNewElem(env);
1374 pessimisticFinalNewElem(env, TInitGen);
1375 push(env, TRef);
1378 //////////////////////////////////////////////////////////////////////
1380 void miFinal(MIS& env, const bc::EmptyM& op) {
1381 // Same thing for now for props and elems, regardless of the base.
1382 // MW would be a fatal.
1383 mcodeKey(env);
1384 miPop(env);
1385 push(env, TBool);
1388 void miFinal(MIS& env, const bc::IssetM& op) {
1389 if (mcodeIsProp(env.mcode())) return miFinalIssetProp(env);
1390 // Elem case (MW would be a fatal):
1391 if (env.mcode() != MemberCode::MW) mcodeKey(env);
1392 miPop(env);
1393 push(env, TBool);
1396 void miFinal(MIS& env, const bc::CGetM& op) {
1397 if (mcodeIsProp(env.mcode())) return miFinalCGetProp(env);
1398 if (env.mcode() == MemberCode::MW) {
1399 // MW is a fatal.
1400 miPop(env);
1401 push(env, TInitCell);
1402 return;
1404 return miFinalCGetElem(env);
1407 void miFinal(MIS& env, const bc::VGetM& op) {
1408 if (mcodeIsElem(env.mcode())) return miFinalVGetElem(env);
1409 if (mcodeIsProp(env.mcode())) return miFinalVGetProp(env);
1410 return miFinalVGetNewElem(env);
1413 void miFinal(MIS& env, const bc::SetM& op) {
1414 if (mcodeIsElem(env.mcode())) return miFinalSetElem(env);
1415 if (mcodeIsProp(env.mcode())) return miFinalSetProp(env);
1416 return miFinalSetNewElem(env);
1419 void miFinal(MIS& env, const bc::SetOpM& op) {
1420 if (mcodeIsElem(env.mcode())) return miFinalSetOpElem(env);
1421 if (mcodeIsProp(env.mcode())) return miFinalSetOpProp(env, op.subop);
1422 return miFinalSetOpNewElem(env);
1425 void miFinal(MIS& env, const bc::IncDecM& op) {
1426 if (mcodeIsElem(env.mcode())) return miFinalIncDecElem(env);
1427 if (mcodeIsProp(env.mcode())) return miFinalIncDecProp(env);
1428 return miFinalIncDecNewElem(env);
1431 void miFinal(MIS& env, const bc::BindM& op) {
1432 if (mcodeIsElem(env.mcode())) return miFinalBindElem(env);
1433 if (mcodeIsProp(env.mcode())) return miFinalBindProp(env);
1434 return miFinalBindNewElem(env);
1437 void miFinal(MIS& env, const bc::UnsetM& op) {
1438 if (mcodeIsElem(env.mcode())) return miFinalUnsetElem(env);
1439 if (mcodeIsProp(env.mcode())) return miFinalUnsetProp(env);
1440 // The MW case is a fatal.
1441 miPop(env);
1444 void miFinal(MIS& env, const bc::SetWithRefLM& op) {
1445 moveBase(env, folly::none);
1446 killLocals(env);
1447 killThisProps(env);
1448 killSelfProps(env);
1449 if (env.mcode() != MemberCode::MW) mcodeKey(env);
1450 miPop(env);
1453 void miFinal(MIS& env, const bc::SetWithRefRM& op) {
1454 moveBase(env, folly::none);
1455 killLocals(env);
1456 killThisProps(env);
1457 killSelfProps(env);
1458 if (env.mcode() != MemberCode::MW) mcodeKey(env);
1459 popR(env);
1460 miPop(env);
1463 //////////////////////////////////////////////////////////////////////
1465 template<class Op>
1466 void miImpl(ISS& baseEnv, const Op& op,
1467 const MInstrInfo& info, const MVector& mvec) {
1468 MIS env { baseEnv, &info, mvec };
1469 env.base = miBase(env);
1470 FTRACE(3, " base: {}\n", base_string(env.base));
1471 miThrow(env);
1472 for (; env.mInd < mvec.mcodes.size() - 1; ++env.mInd) {
1473 miIntermediate(env);
1474 FTRACE(3, " base: {}\n", base_string(env.base));
1475 miThrow(env);
1477 miFinal(env, op);
1479 // If the final operation was either a define, a NewElem, or an
1480 // unset, there may be pending effects in local array chains or on
1481 // local types, so we need to move the base one last time. The
1482 // other cases should have no effects here.
1483 moveBase(env, folly::none);
1486 //////////////////////////////////////////////////////////////////////
1490 #define MII(m, ...) \
1491 void minstr(ISS& env, const bc::m##M& op) { \
1492 miImpl(env, op, getMInstrInfo(Op::m##M), op.mvec); \
1494 MINSTRS
1495 #undef MII
1497 //////////////////////////////////////////////////////////////////////