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;
}