Typeref verify_eval()

in modules/asc/src/java/adobe/abc/GlobalOptimizer.java [5835:6262]


	Typeref verify_eval(Method m, Expr e,
			Map<Expr,Typeref> types, Map<Block,Block> idom)
	{
		Typeref tref = null;
				
		if (e.op == OP_phi)
		{
			boolean loop = false;
			for (int i=e.args.length-1; i >= 0; i--)
			{
				Expr a = e.args[i];
				loop |= isLoop(e.pred[i],idom);
				// compute the phi() meet operation.  ignore any inputs we
				// haven't evaluated yet.  If all inputs have the same value,
				// phi() has that value.  Otherwise return BOTTOM.
				
				// same idea for types, but if they aren't all the same
				// then compute the most derived base class (mdb) of the types.
				Typeref aref = types.get(a);
				if (aref == null)
					continue; // ignore unprocessed inputs
				if (tref == null)
					tref = aref;
				else if (!tref.equals(aref))
					tref = mdb(tref,aref);
			}
			
			if ( null == tref )
			{
				tref = ANY().ref;
			}
			// make nullable types nullable at top of loop so verifier
			// won't ever need to iterate.
			if (loop)
				tref = tref.t.ref;
		}
		else
		{
			tref = ANY().ref;
			
			// if any arg is TOP result is TOP (unchanged)
			for (Expr a : e.args) if (!types.containsKey(a))	return tref;
			for (Expr a : e.scopes) if (!types.containsKey(a))	return tref;
			for (Expr a : e.locals) if (!types.containsKey(a))	return tref;
			
			switch (e.op)
			{
			default:
				assert(false);
			
			case OP_hasnext2_o:
			case OP_nextname:
			case OP_nextvalue:
			case OP_call:
			case OP_getsuper:
			case OP_getdescendants:
				tref = ANY().ref;
				break;
				
			case OP_convert_o:
				// bug in verifier -- doesn't set state nonnull here.
				tref = types.get(e.args[0]);
				//tref = types.get(e.args[0]).nonnull();
				break;
			
			case OP_esc_xattr:
			case OP_esc_xelem:
				tref = STRING().ref.nonnull();
				break;
				
			case OP_newcatch:
				tref = m.handlers[e.imm[0]].activation;
				break;
				
			case OP_newobject:
				tref = OBJECT().ref.nonnull();
				break;
			
			case OP_newarray:
				tref = ARRAY().ref.nonnull();
				break;
				
			case OP_newactivation:
				tref = m.activation;
				break;
				
			case OP_getglobalscope:
				if (m.cx.scopes.length > 0)
				{
					tref = m.cx.scopes[0];
				}
				else
				{
					// same as getscopeobject<0>
					tref = types.get(e.scopes[0].args[0]);
				}
				break;
				
			case OP_getscopeobject:
				tref = types.get(e.scopes[0].args[0]);
				break;
				
			case OP_newclass:
				tref = e.c.ref.nonnull();
				break;
				
			case OP_newfunction:
				tref = FUNCTION().ref.nonnull();
				break;
				
			case OP_finddef:
				if (TypeCache.instance().globals.contains(e.ref))
					tref = TypeCache.instance().globals.get(e.ref);
				break;
				
			case OP_findpropstrict:
			case OP_findproperty:
			{
				int i = findInner(e.ref, e.scopes, types);
				if (i >= 0)
				{
					tref = types.get(e.scopes[i]);
				}
				else if ((i = findOuter(e.ref, m.cx.scopes)) >= 0)
				{
					tref = m.cx.scopes[i];
				}
				else if (TypeCache.instance().globals.contains(e.ref))
				{
					tref = TypeCache.instance().globals.get(e.ref);
				}
				else
				{
					// not found.  use global.
					if (m.cx.scopes.length > 0)
					{
						tref = m.cx.scopes[0];
					}
					else
					{
						tref = types.get(e.scopes[0]);
					}
				}
				break;
			}
			
			case OP_getlex:
			{
				// findproperty + getproperty
				int i = findInner(e.ref, e.scopes, types);
				Typeref stref = i >= 0 ? types.get(e.scopes[i]) : 
					(i=findOuter(e.ref, m.cx.scopes)) >= 0 ? m.cx.scopes[i] :
						TypeCache.instance().globals.contains(e.ref) ? TypeCache.instance().globals.get(e.ref) :
							m.cx.scopes.length > 0 ? m.cx.scopes[0] :
								types.get(e.scopes[0]);
	
				Binding b = stref.t.findGet(e.ref);
				tref = verify_eval_getproperty(tref, b);
				break;
			}
			
			case OP_construct:
			{
				Type base_type = type(types,e.args[0]);
				
				if ( base_type.itype != null )
					tref = base_type.itype.ref.nonnull();
				else
					tref = base_type.ref.nonnull();
				
				break;
			}
			
			case OP_constructprop:
			{
				Type ot = type(types,e.args[0]); // type of base object
				Binding b = ot.findGet(e.ref);
				if (b != null && b.type != null && b.type.t.itype != null)
				{
					tref = b.type.t.itype.ref.nonnull();
					break;
				}
				break;
			}
			
			case OP_callproperty:
			case OP_callproplex:
			{
				Type ot = type(types, e.args[0]);
				Binding b = ot.findGet(e.ref);
				if (isMethod(b))
				{
					tref = b.method.returns;
				}
				else if (isSlot(b) && b.type != null && b.type.t.itype != null)
				{
					// calling a class as a function, return type is cast to instance type
					// issue what about special cases? RegExp? Date?
					tref = b.type.t.itype.ref;
				}
				break;
			}
			
			case OP_callsuper:
			{
				Type ot = m.cx.base;
				Binding b = ot.findGet(e.ref);
				if (isMethod(b))
				{
					tref = b.method.returns;
				}
				break;
			}
			
			case OP_applytype:
				tref = types.get(e.args[0]).nonnull();
				break;
			
			case OP_callstatic:
				tref = e.m.returns;
				break;
			
			case OP_arg:
				if (e.imm[0] < m.getParams().length)
					tref = m.getParams()[e.imm[0]];
				else if (m.needsArguments()||m.needsRest() && e.imm[0] == m.getParams().length)
					tref = ARRAY().ref.nonnull();
				else
					tref = VOID().ref;
				break;
				
			case OP_xarg:
				tref = m.handlers[e.imm[0]].type;
				break;
				
			case OP_getslot:
			{
				Type t0 = type(types, e.args[0]);
				Binding b = t0.findSlot(e.imm[0]);
				if (b != null)
					tref = b.type;
				break;
			}
			
			case OP_getproperty:
			{
				Type t0 = type(types, e.args[0]);
				Binding b = t0.findGet(e.ref);
				tref = verify_eval_getproperty(tref, b);
				break;
			}
			
			case OP_kill:
				tref = ANY().ref;
				break;
				
			case OP_pushundefined:
				tref = VOID().ref;
				break;
				
			case OP_pushnull:
				tref = NULL().ref;
				break;

			case OP_pushnamespace:
				tref = NAMESPACE().ref.nonnull();
				break;
							
			case OP_pushscope:
			case OP_pushwith:
				// treat this as a copy.
				tref = types.get(e.args[0]).nonnull();
				break;
				
			case OP_pushtrue:
			case OP_pushfalse:
			case OP_convert_b:
			case OP_not:
			case OP_deleteproperty:
			case OP_deldescendants:
			case OP_hasnext:
			case OP_hasnext2:
			case OP_equals:
			case OP_strictequals:
			case OP_in:
			case OP_istype:
			case OP_istypelate:
			case OP_instanceof:
			case OP_lessthan:
			case OP_lessequals:
			case OP_greaterthan:
			case OP_greaterequals:
				tref = BOOLEAN().ref;
				break;
			
			case OP_convert_s:
			case OP_pushstring:
				tref = STRING().ref.nonnull();
				break;
			
			case OP_coerce_s:
			{
				Typeref t0 = types.get(e.args[0]);
				tref = new Typeref(STRING(), t0.nullable);
				break;
			}

			case OP_coerce_o:
			{
				Typeref t0 = types.get(e.args[0]);
				tref = new Typeref(OBJECT(), t0.nullable);
				break;
			}

			case OP_coerce_a:
			{
				Typeref t0 = types.get(e.args[0]);
				tref = new Typeref(ANY(), t0.nullable);
				break;
			}
			
			case OP_coerce:
				tref = TypeCache.instance().namedTypes.get(e.ref).ref;
				break;
			
			case OP_astype:
			{
				Typeref t0 = types.get(e.args[0]);
				Type t = TypeCache.instance().namedTypes.get(e.ref);
				if (!t0.t.extendsOrIsBase(t) || t0.t.isAtom() != t.isAtom())
				{
					// TODO figure out what verifier is really doing here.
					tref = t.ref;
				}
				else
				{
					tref = t0;
				}
				break;
			}
			
			case OP_astypelate:
			{
				Typeref t1 = types.get(e.args[1]);
				if (t1.t.itype != null)
				{
					if (t1.t.itype.atom || t1.t.itype.numeric)
						tref = OBJECT().ref;
					else
						tref = t1.t.itype.ref;
				}
				else
				{
					tref = ANY().ref;
				}
				break;
			}
				
			case OP_typeof:
				tref = STRING().ref.nonnull();
				break;
			
			case OP_add:
			{
				Expr a0 = e.args[0];
				Expr a1 = e.args[1];
				Typeref t0 = types.get(a0);
				Typeref t1 = types.get(a1);
				if (t0.t == STRING() && !t0.nullable || t1.t == STRING() && !t1.nullable)
				{
					tref = STRING().ref.nonnull();
				}
				else if (t0.t.numeric && t1.t.numeric)
				{
					tref = NUMBER().ref;
				}
				else
				{
					// TODO make all primitives extend a type so we can use that type here.
					tref = OBJECT().ref.nonnull(); // will be a String or a Number
				}
				break;
			}
			
			case OP_divide:
			case OP_subtract:
			case OP_multiply:
			case OP_modulo:
			case OP_negate:
			case OP_increment:
			case OP_decrement:
			case OP_convert_d:
			case OP_pushnan:
			case OP_pushdouble:
				tref = NUMBER().ref;
				break;

			case OP_convert_i:
			case OP_bitor:
			case OP_bitand:
			case OP_add_i:
			case OP_subtract_i:
			case OP_multiply_i:
			case OP_negate_i:
			case OP_bitxor:
			case OP_lshift:
			case OP_rshift:
			case OP_hasnext2_i:
			case OP_increment_i:
			case OP_decrement_i:
			case OP_pushbyte:
			case OP_pushshort:
			case OP_pushint:
			case OP_bitnot:
				tref = INT().ref;
				break;
				
			case OP_pushuint:
			case OP_convert_u:
			case OP_urshift:
				tref = UINT().ref;
				break;
			}
		}
		
		assert(tref != null && tref.t != null);
		return tref;
	}