void sccp_eval()

in modules/asc/src/java/adobe/abc/GlobalOptimizer.java [5107:5833]


	void sccp_eval(Method m, Expr e, 
			Map<Expr,Object> values, 
			Map<Expr,Typeref> types,
			Set<Edge>flowWork, Set<Expr>ssaWork, 
			EdgeMap<Expr> uses)
	{
		Object v = null;
		Typeref tref = null;
		
		if (e.op == OP_phi)
		{
			for (Expr a: e.args)
			{
				// 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.
				Object av = values.get(a);
				if (av == null) continue; // ignore TOP inputs
				
				if (v == null)
					v = av;
				else if (!av.equals(v))
					v = 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 (tref == null)
					tref = aref;
				else if (!tref.equals(aref))
					tref = mdb(tref,aref);
			}
		}
		else
		{
			// if any arg is TOP result is TOP (unchanged)
			for (Expr a : e.args) if (!values.containsKey(a))	return;
			for (Expr a : e.scopes) if (!values.containsKey(a))	return;
			for (Expr a : e.locals) if (!values.containsKey(a))	return;
			
			v = BOTTOM;
			tref = ANY().ref;
			
			switch (e.op)
			{
			default:
				System.err.println("unhandled op:" + e.op + ":"+ opNames[e.op]);
				assert(false);
			
			case OP_hasnext2_o:
			case OP_nextname:
			case OP_nextvalue:
			case OP_call:
			case OP_callsuper:
			case OP_getsuper:
			case OP_getdescendants:
				break;
				
			case OP_convert_o:
			{
				tref = types.get(e.args[0]).nonnull();
				v = values.get(e.args[0]);
				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>
					v = values.get(e.scopes[0].args[0]);
					tref = types.get(e.scopes[0].args[0]);
				}
				break;
				
			case OP_getscopeobject:
				v = values.get(e.scopes[0].args[0]);
				tref = types.get(e.scopes[0].args[0]);
				if ( tref == null )
				{
					//  FIXME: Should be more thorough.
					tref = ANY().ref;
				}
				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)
				{
					v = values.get(e.scopes[i]);
					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
					{
						v = values.get(e.scopes[0]);
						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);
				// code below is a copy of OP_getproperty
				if (isSlot(b))
				{
					// TODO we only compute const value here if primitive type.
					// it would be more correct if we knew whether the initializer
					// changed the const value.  (consts can be computed in init).
					tref = b.type;
					if (isConst(b) && b.defaultValueChanged())
						v = b.value;
				}
				else if (isMethod(b))
				{
					// TODO if base type is or might be XML, return ANY
					// TODO use MethodClosure, more specific than Function
					tref = FUNCTION().ref.nonnull();
				}
				else if (isGetter(b))
				{
					tref = b.method.returns;
				}
				break;
			}
			
			case OP_construct:
			{
				tref = OBJECT().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)
				{
					// each of these has same logic as convert_i, convert_s, etc
					if (b.type.t.itype == INT()) 
					{
						tref = INT().ref;
						if ( e.args.length > 1)
							v = eval_convert_i(values.get(e.args[1]));
					}
					else if (b.type.t.itype == UINT()) 
					{
						tref = UINT().ref;
						if ( e.args.length > 1)
							v = eval_convert_u(values.get(e.args[1]));
					}
					else if (b.type.t.itype == STRING())
					{
						tref = STRING().ref.nonnull();
						if ( e.args.length > 1)
							v = eval_convert_s(values.get(e.args[1]));
					}
					else if (b.type.t.itype == BOOLEAN())
					{
						tref = BOOLEAN().ref;
						if ( e.args.length > 1)
							v = eval_convert_b(values.get(e.args[1]));
					}
					else if (b.type.t.itype == NUMBER())
					{
						tref = NUMBER().ref;
						if ( e.args.length > 1)
							v = eval_convert_d(values.get(e.args[1]));
					}
				}
				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);
				if (isSlot(b))
				{
					// TODO we only compute const value here if primitive type.
					// it would be more correct if we knew whether the initializer
					// changed the const value.  (consts can be computed in init).
					tref = b.type;
					if (isConst(b) && b.defaultValueChanged())
						v = b.value;
				}
				else if (isMethod(b))
				{
					// TODO if base type is or might be XML, return ANY
					// TODO use MethodClosure, more specific than Function
					tref = FUNCTION().ref.nonnull();
				}
				else if (isGetter(b))
				{
					tref = b.method.returns;
				}
				break;
			}
			
			case OP_pushundefined:
				v = e.value;
				tref = VOID().ref;
				break;
				
			case OP_pushnull:
				v = e.value;
				tref = NULL().ref;
				break;
				
			case OP_pushtrue:
			case OP_pushfalse:
				v = e.value;
				tref = BOOLEAN().ref;
				break;
				
			case OP_pushbyte:
			case OP_pushshort:
			case OP_pushint:
				v = e.value;
				tref = INT().ref;
				break;
				
			case OP_pushuint:
				v = e.value;
				tref = UINT().ref;
				break;
				
			case OP_pushstring:
				v = e.value;
				tref = STRING().ref.nonnull();
				break;
				
			case OP_pushnan:
			case OP_pushdouble:
				v = e.value;
				tref = NUMBER().ref;
				break;
				
			case OP_pushnamespace:
				v = e.value;
				tref = NAMESPACE().ref.nonnull();
				break;
				
			case OP_jump:
				flowWork.add(e.succ[0]);
				return;
	
			case OP_lookupswitch:
			{
				Object v1 = values.get(e.args[0]);
				if (v1 == BOTTOM)
					for (Edge s: e.succ)
						flowWork.add(s);
				else
				{
					// input is const
					int i = intValue(v1);
					if (i < 0 || i >= e.succ.length-1) 
						i = e.succ.length-1;
					flowWork.add(e.succ[i]);
				}
				return;
			}
			
			case OP_iffalse:
			case OP_iftrue:
			{
				Object v1 = values.get(e.args[0]);
				if (v1 == BOTTOM)
				{
					flowWork.add(e.succ[0]);
					flowWork.add(e.succ[1]);
				}
				else if (e.op == OP_iffalse)
					flowWork.add(e.succ[booleanValue(v1) ? 0 : 1]);
				else if (e.op == OP_iftrue)
					flowWork.add(e.succ[booleanValue(v1) ? 1 : 0]);
				return;
			}
			
			case OP_pushscope:
			case OP_pushwith:
				// treat this as a copy.
				v = values.get(e.args[0]);
				tref = types.get(e.args[0]).nonnull();
				break;
				
			case OP_convert_b:
				tref = BOOLEAN().ref;
				v = eval_convert_b(values.get(e.args[0]));
				break;
				
			case OP_not:
			{
				tref = BOOLEAN().ref;
				Object v0 = values.get(e.args[0]);
				if (v0 != BOTTOM)
					v = booleanValue(v0) ? FALSE : TRUE;
				break;
			}
				
			case OP_deleteproperty:
				// TODO result is const false for any declared property
			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:
				tref = BOOLEAN().ref;
				break;
				
			case OP_lessthan:
			case OP_lessequals:
			case OP_greaterthan:
			case OP_greaterequals:
			{
				tref = BOOLEAN().ref;
				Object v0 = values.get(e.args[0]);
				Object v1 = values.get(e.args[1]);
				if (v0.equals(NAN) || v0 == UNDEFINED || v1.equals(NAN) || v1 == UNDEFINED)
					v = FALSE;
				else if (v0 != BOTTOM && v1 != BOTTOM)
					v = e.op == OP_lessthan ?     lessthan(v0,v1) :
						e.op == OP_lessequals ?  !lessthan(v1,v0) :
					    e.op == OP_greaterthan ?  lessthan(v1,v0) :
					    	                     !lessthan(v0,v1);
				break;
			}
			
			case OP_convert_s:
				tref = STRING().ref.nonnull();
				v = eval_convert_s(values.get(e.args[0]));
				break;
			
			case OP_coerce_s:
			{
				tref = eval_coerce_s(types.get(e.args[0]));
				v = eval_coerce_s(values.get(e.args[0]));
				break;
			}

			case OP_coerce_o:
			{
				Typeref t0 = types.get(e.args[0]);
				tref = eval_coerce_o(t0);
				v = eval_coerce_o(values.get(e.args[0]), t0.t);
				break;
			}

			case OP_coerce_a:
			{
				//  This cast has meaning if it's casting from void.
				//  Otherwise, it's an upcast and can be removed;
				//  casts will be re-inserted as appropriate.
				if ( ! (types.get(e.args[0]).equals(VOID().ref) ) )
				{
					v = values.get(e.args[0]);
					tref = types.get(e.args[0]);
				}
				else
				{
					tref = ANY().ref;
				}
				break;
			}
			
			case OP_coerce:
			{
				Typeref t0 = types.get(e.args[0]);
				Object v0 = values.get(e.args[0]);
				Type t = TypeCache.instance().namedTypes.get(e.ref);
				assert ( t != null );
				
				if (t == STRING())
				{
					tref = eval_coerce_s(t0);
					v = eval_coerce_s(v0);
				}
				else if (t == OBJECT())
				{
					tref = eval_coerce_o(t0);
					v = eval_coerce_o(v0,t0.t);
				}
				else if (t == INT())
				{
					tref = t.ref;
					v = eval_convert_i(v0);
				}
				else if (t == UINT()) 
				{
					tref = t.ref;
					v = eval_convert_u(v0);
				}
				else if (t == NUMBER()) 
				{
					tref = t.ref;
					v = eval_convert_d(v0);
				}
				else if (t == BOOLEAN()) 
				{
					tref = t.ref;
					v = eval_convert_b(v0);
				}
				else
				{
					// pointer style cast
					if (t0.t.extendsOrIsBase(t))
					{
						// ignore upcasts
						tref = t0;
						v = v0;
					}
					else if (t0.t == NULL() || t0.t == VOID())
					{
						tref = NULL().ref;
					}
					else
					{
						tref = t.ref;
					}
				}
				break;
			}
			
			case OP_astype:
				// TODO constant folding
				tref = TypeCache.instance().namedTypes.get(e.ref).ref;
				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:
			{
				Type t0 = type(types,e.args[0]);
				if (t0 == INT() || t0 == UINT() || t0 == NUMBER())
					v = "number";
				else if (t0 == STRING())
					v = "string";
				else if (t0.extendsOrIsBase(XML()) || t0.extendsOrIsBase(XMLLIST()))
					v = "xml";
				else if (t0 == VOID())
					v = "undefined";
				else if (t0 == BOOLEAN())
					v = "boolean";
				else if (t0.extendsOrIsBase(FUNCTION()))
					v = "function";
				else if (t0 != OBJECT() && t0.extendsOrIsBase(OBJECT()))
					v = "object";
				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);
				Object v0 = values.get(a0);
				Object v1 = values.get(a1);
				if (t0.t == STRING() && !t0.nullable || t1.t == STRING() && !t1.nullable)
				{
					tref = STRING().ref.nonnull();
					if (v0 != BOTTOM && v1 != BOTTOM)
						v = stringValue(v0) + stringValue(v1);
				}
				else if (t0.t.numeric && t1.t.numeric)
				{
					tref = NUMBER().ref;
					if (v0 instanceof Number && v1 instanceof Number)
						v = doubleValue(v0) + doubleValue(v1);
				}
				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:
			{
				tref = NUMBER().ref;
				Object v0 = values.get(e.args[0]);
				Object v1 = values.get(e.args[1]);
				if (v0 instanceof Number && v1 instanceof Number)
					v = doubleValue(v0) / doubleValue(v1);
				break;
			}
			
			case OP_subtract:
			case OP_multiply:
			case OP_modulo:
			case OP_negate:
			case OP_increment:
			case OP_decrement:
				tref = NUMBER().ref;
				break;

			case OP_convert_d:
				tref = NUMBER().ref;
				v = eval_convert_d(values.get(e.args[0]));
				break;
				
			case OP_convert_i:
				tref = INT().ref;
				v = eval_convert_i(values.get(e.args[0]));
				break;

			case OP_convert_u:
				tref = UINT().ref;
				v = eval_convert_u(values.get(e.args[0]));
				break;
	
			case OP_bitor:
			{
				tref = INT().ref;
				Object v0 = values.get(e.args[0]);
				Object v1 = values.get(e.args[1]);
				if (v0 instanceof Number && v1 instanceof Number)
					v = intValue(v0) | intValue(v1);
				break;
			}
			
			case OP_bitand:
			{
				tref = INT().ref;
				Object v0 = values.get(e.args[0]);
				Object v1 = values.get(e.args[1]);
				if (v0 instanceof Number && v1 instanceof Number)
				{
					v = intValue(v0) & intValue(v1);
				}
				break;
			}
				
			case OP_bitnot:
			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:
				// TODO constant folding
				tref = INT().ref;
				break;
				
			case OP_urshift:
				// TODO constant folding
				tref = UINT().ref;
				break;
				
			// these ops do not produce any value
			case OP_setslot:
			case OP_setproperty:
			case OP_setsuper:
			//case OP_setglobalslot: // deprecated
			case OP_initproperty:
			case OP_callpropvoid:
			case OP_constructsuper:
			case OP_callsupervoid:
			case OP_returnvoid:
			case OP_returnvalue:
			case OP_throw:
			case OP_popscope:
			case OP_debug:
			case OP_debugline:
			case OP_debugfile:
			case OP_bkpt:
			case OP_bkptline:
			case OP_checkfilter:
				return;
			}
		}
		
		assert(tref != null && tref.t != null);
		
		// singleton types have a specific value.
		if (tref.t == VOID())
			v = UNDEFINED;
		else if (tref.t == NULL())
			v = NULL();
		
		if (v != null && !v.equals(values.get(e)))
		{
			values.put(e, v);
			ssaWork.addAll(uses.get(e));
		}
		
		if (!tref.equals(types.get(e)))
		{
			types.put(e, tref);
			ssaWork.addAll(uses.get(e));
		}
	}