in modules/asc/src/java/macromedia/asc/semantics/ConstantEvaluator.java [841:1033]
public Value evaluate(Context cx, SetExpressionNode node)
{
// Without a type annotation, the expected type of the definition
// is the union of types of all uses of this definition, except if
// this is an indexed put, then it is Object because we can't be
// sure we have seen all assignments.
if (node.base == null && !node.expr.isLValue())
{
cx.error(node.pos(),kError_AssignmentToNonRefVar);
}
else
{
if (node.ref != null)
{
if(node.is_initializer)
{
node.ref.calcUseDefinitions(cx, rch_bits);
if (!node.ref.usedBeforeInitialized())
{
// If the slot is set before it is declared/initialized we need to init
// it at the top of the method to get the types to agree at branch targets
// This happens with code like:
//
// if( something() )
// x = "blah";
// var x : String = "hi";
//
// We need to init this at the top of the method, otherwise the types for
// x at the end of the if block would be * and String, which wouldn't match
// and would cause a verify error.
Slot s = node.ref.getSlot(cx,GET_TOKEN);
if (s != null)
s.setNeedsInit(true);
}
}
Slot slot = node.ref.getSlot(cx,SET_TOKEN);
node.ref.getType(cx,SET_TOKEN);
if( slot != null )
{
// need to check var_index to see if this is a setter. In the case of slots inherited during abc import,
// this will only be accurate for the original slot
Slot origSlot = slot;
if( origSlot.getVarIndex() < 0 && size(slot.getTypes()) == 1 )
{
node.args.addType(slot.getTypes().get(0)); // setter, expected type is param type
}
else
{
node.args.addType(slot.getType());
}
}
else
{
node.args.addType(cx.noType().getDefaultTypeInfo());
}
}
else if( node.base != null && node.getMode()==LEFTBRACKET_TOKEN )
{
node.expr.evaluate(cx, this);
TypeInfo t = cx.noType().getDefaultTypeInfo();
if( node.base.type != null )
{
TypeValue tv = node.base.type.getTypeValue();
t = tv.indexed_type != null ? tv.indexed_type.getDefaultTypeInfo() : cx.noType().getDefaultTypeInfo();
}
node.args.addType(t);
}
else
{
node.expr.evaluate(cx, this); // Only do this if there is no ref.
node.args.addType(cx.noType().getDefaultTypeInfo());
}
}
// rch_bits = rch_bits & ~ node.gen_bits; // temporarily turn off the current gen bits
Value val = node.args.evaluate(cx, this);
TypeInfo type = val != null ? val.getType(cx) : cx.noType().getDefaultTypeInfo();
if ( cx.useStaticSemantics() && node.ref != null )
{
Slot slot = node.ref.getSlot(cx,SET_TOKEN);
int rchkill_bits_count = BitSet.and_count(rch_bits, node.getKillBits()); // number of kill bits that reach this definition, should be zero
int scope_index = node.ref.getScopeIndex(GET_TOKEN);
int base_index = cx.getScopes().size()-1;
if (slot != null && slot.isConst() && slot.getType().getTypeValue() == cx.typeType())
{
Node firstArg = node.args.items.get(0);
// check if its the synthetic assignment invented for a CDN. Authors can't assing a var directly to a CDN
if (!(firstArg instanceof ClassDefinitionNode))
{
if (slot.getObjectValue() != null) // slot will only have a value if this is a class slot
{
cx.error(node.pos(), kError_AssignmentToDefinedClass, node.ref.name);
}
}
}
else if (slot != null && slot.isConst() && slot.getType().getTypeValue() == cx.functionType())
{
Node firstArg = node.args.items.get(0);
// check if its the synthetic assignment invented for a FDN.
if (!(firstArg instanceof FunctionCommonNode && ((FunctionCommonNode)firstArg).def != null) )
{
// if the base type is XML or XMLList, ignore. The prop may actually evaluate to a non-function at runtime (for instance, .name)
boolean isXMLProp = false;
ObjectValue base = node.ref.getBase();
if (base != null &&
(base.getType(cx).getTypeValue() == cx.xmlType() ||
base.getType(cx).getTypeValue() == cx.xmlListType()))
{
isXMLProp = true;
}
if (!isXMLProp && slot.getObjectValue() != null)
{
cx.error(node.pos(), kError_AssignmentToDefinedFunction, node.ref.name);
}
}
}
else if( slot != null && slot.isConst() && (slot.isImported() || scope_index != base_index || val.hasValue() || rchkill_bits_count > 0) )
{
cx.error(node.pos(), kError_AssignmentToConstVar);
}
else if( cx.useStaticSemantics() && slot == null )
{
// If there is no set but there is a get, then the property is read only. Post an error.
slot = node.ref.getSlot(cx,GET_TOKEN);
if ( slot != null )
{
// slot will only have a value if this is a slot for a non-anonymous function
if( slot.getType() != null && slot.getType().getTypeValue() == cx.functionType() && slot.getObjectValue() != null )
{
Node firstArg = node.args.items.get(0);
CoerceNode cn = (firstArg instanceof CoerceNode) ? (CoerceNode)firstArg : null;
if ( !firstArg.isSynthetic() || (cn != null && !(cn.expr instanceof FunctionCommonNode) ) ) // its not the synthetic assignment invented for a FDN. Authors can't assing a var directly to a FCN
{
boolean isXMLProp = false;
ObjectValue base = node.ref.getBase();
if (base != null &&
(base.getType(cx).getTypeValue() == cx.xmlType() ||
base.getType(cx).getTypeValue() == cx.xmlListType()))
{
isXMLProp = true;
}
if (!isXMLProp)
{
int pos = node.pos() == 0 ? node.expr.pos() : node.pos();
cx.error(pos, kError_AssignmentToDefinedFunction, node.ref.name);
}
}
}
else
{
cx.error(node.pos(), kError_PropertyIsReadOnly);
}
}
else
{
ObjectValue base = node.ref.getBase();
// Note: only global Functions are dynamic, but methods of a class are MethodClosures, a non-dynamic subclass of Function.
if( base != null && (!base.isDynamic() || (base.getType(cx).getTypeValue() == cx.functionType() && !(base.builder instanceof GlobalBuilder)) ) )
{
if (base.hasNameUnqualified(cx, node.ref.name, GET_TOKEN))
cx.error(node.expr.pos(), kError_InaccessiblePropertyReference, node.ref.name, base.getType(cx).getName(cx).toString());
else
cx.error(node.expr.pos(), kError_UndefinedProperty,node.ref.name,base.getType(cx).getName(cx).toString());
}
}
}
}
cx.setDefType(node.gen_bits, type);
rch_bits = reset_set(rch_bits, node.getKillBits(), node.getGenBits());
if (node.ref != null)
{
node.ref.calcUseDefinitions(cx, rch_bits);
}
node.value_type = type; // for use at code gen time.
return val;
}