CiExpr ICiExprVisitor.Visit(CiSymbolAccess expr)
{
CiSymbol symbol = Lookup(expr);
if (symbol is CiVar)
{
return new CiVarAccess {
Var = (CiVar)symbol
}
}
;
if (symbol is CiConst)
{
return(GetValue((CiConst)symbol));
}
if (symbol is CiField)
{
if (this.CurrentMethod.CallType == CiCallType.Static)
{
throw new ResolveException("Cannot access field from a static method");
}
symbol.Accept(this);
return(CreateFieldAccess(new CiVarAccess {
Var = this.CurrentMethod.This
}, (CiField)symbol));
}
throw new ResolveException("Invalid expression");
}
CiExpr ICiExprVisitor.Visit(CiUnknownMemberAccess expr)
{
if (expr.Parent is CiSymbolAccess)
{
CiSymbol symbol = Lookup((CiSymbolAccess)expr.Parent);
if (symbol is CiEnum)
{
return(new CiConstExpr(((CiEnum)symbol).LookupMember(expr.Name)));
}
if (symbol is CiClass)
{
symbol = ((CiClass)symbol).Members.Lookup(expr.Name);
if (symbol is CiConst)
{
return(GetValue((CiConst)symbol));
}
throw new ResolveException("Cannot access " + expr.Name);
}
}
CiExpr parent = Resolve(expr.Parent);
CiSymbol member = parent.Type.LookupMember(expr.Name);
member.Accept(this);
if (member is CiField)
{
return(CreateFieldAccess(parent, (CiField)member));
}
if (member is CiProperty)
{
CiProperty prop = (CiProperty)member;
if (parent is CiConstExpr)
{
if (prop == CiLibrary.LowByteProperty)
{
return(new CiConstExpr((byte)GetConstInt(parent)));
}
if (prop == CiLibrary.SByteProperty)
{
return(new CiConstExpr((int)(sbyte)GetConstInt(parent)));
}
if (prop == CiLibrary.StringLengthProperty)
{
return(new CiConstExpr(((string)((CiConstExpr)parent).Value).Length));
}
}
return(new CiPropertyAccess {
Obj = parent, Property = prop
});
}
if (member is CiConst)
{
return(new CiConstExpr(((CiConst)member).Value));
}
throw new ResolveException(member.ToString());
}
CiExpr ICiExprVisitor.Visit(CiIndexAccess expr)
{
CiExpr parent = Resolve(expr.Parent);
CiExpr index = Coerce(Resolve(expr.Index), CiIntType.Value);
if (parent.Type is CiArrayType)
{
return new CiArrayAccess {
Array = parent, Index = index
}
}
;
if (parent.Type is CiStringType)
{
if (parent is CiConstExpr && index is CiConstExpr)
{
string s = (string)((CiConstExpr)parent).Value;
int i = GetConstInt(index);
if (i < s.Length)
{
return(new CiConstExpr((int)s[i]));
}
}
return(new CiMethodCall {
Method = CiLibrary.CharAtMethod,
Obj = parent,
Arguments = new CiExpr[1] {
index
}
});
}
throw new ResolveException("Indexed object is neither array or string");
}
void ICiSymbolVisitor.Visit(CiDelegate del)
{
del.ReturnType = Resolve(del.ReturnType);
foreach (CiParam param in del.Params)
{
param.Type = Resolve(param.Type);
}
}
CiType ICiTypeVisitor.Visit(CiDelegate del)
{
((ICiSymbolVisitor)this).Visit(del);
return(del);
}
void ResolveObj(CiMethodCall expr)
{
if (expr.Obj is CiSymbolAccess)
{
// Foo(...)
CiMethod method = Lookup((CiSymbolAccess)expr.Obj) as CiMethod;
if (method != null)
{
expr.Method = method;
if (method.CallType == CiCallType.Static)
{
expr.Obj = null;
}
else
{
if (this.CurrentMethod.CallType == CiCallType.Static)
{
throw new ResolveException("Cannot call instance method from a static method");
}
expr.Obj = Coerce(new CiVarAccess {
Var = this.CurrentMethod.This
}, new CiClassPtrType {
Class = method.Class
});
CheckCopyPtr(method.This.Type, expr.Obj);
}
return;
}
}
else if (expr.Obj is CiUnknownMemberAccess)
{
// ???.Foo(...)
CiUnknownMemberAccess uma = (CiUnknownMemberAccess)expr.Obj;
if (uma.Parent is CiSymbolAccess)
{
CiClass klass = Lookup((CiSymbolAccess)uma.Parent) as CiClass;
if (klass != null)
{
// Class.Foo(...)
CiMethod method = klass.Members.Lookup(uma.Name) as CiMethod;
if (method != null)
{
if (method.CallType != CiCallType.Static)
{
throw new ResolveException("{0} is a non-static method", method.Name);
}
expr.Method = method;
expr.Obj = null;
return;
}
}
}
CiExpr obj = Resolve(uma.Parent);
{
CiMethod method = obj.Type.LookupMember(uma.Name) as CiMethod;
if (method != null)
{
// obj.Foo(...)
if (method.CallType == CiCallType.Static)
{
throw new ResolveException("{0} is a static method", method.Name);
}
if (method.This != null)
{
// user-defined method
CheckCopyPtr(method.This.Type, obj);
obj = Coerce(obj, new CiClassPtrType {
Class = method.Class
});
}
expr.Method = method;
expr.Obj = obj;
return;
}
}
}
expr.Obj = Resolve(expr.Obj);
if (!(expr.Obj.Type is CiDelegate))
{
throw new ResolveException("Invalid call");
}
if (expr.Obj.HasSideEffect)
{
throw new ResolveException("Side effects not allowed in delegate call");
}
}
void CoerceArguments(CiMethodCall expr)
{
expr.Signature.Accept(this);
CiParam[] paramz = expr.Signature.Params;
if (expr.Arguments.Length != paramz.Length)
{
throw new ResolveException("Invalid number of arguments for {0}, expected {1}, got {2}", expr.Signature.Name, paramz.Length, expr.Arguments.Length);
}
for (int i = 0; i < paramz.Length; i++)
{
CiExpr arg = Resolve(expr.Arguments[i]);
CheckCopyPtr(paramz[i].Type, arg);
expr.Arguments[i] = Coerce(arg, paramz[i].Type);
}
}
CiExpr ICiExprVisitor.Visit(CiMethodCall expr)
{
ResolveObj(expr);
CoerceArguments(expr);
if (expr.Method != null && expr.Method != this.CurrentMethod)
{
if (expr.Method.IsMutator)
{
MarkWritable(expr.Obj);
}
expr.Method.CalledBy.Add(this.CurrentMethod);
this.CurrentMethod.Calls.Add(expr.Method);
}
return(expr);
}
CiExpr ICiExprVisitor.Visit(CiUnaryExpr expr)
{
CiExpr resolved;
if (expr.Op == CiToken.Increment || expr.Op == CiToken.Decrement)
{
resolved = ResolveLValue(expr.Inner);
}
else
{
resolved = Resolve(expr.Inner);
}
expr.Inner = Coerce(resolved, CiIntType.Value);
if (expr.Op == CiToken.Minus && expr.Inner is CiConstExpr)
{
return(new CiConstExpr(-GetConstInt(expr.Inner)));
}
return(expr);
}
CiExpr ICiExprVisitor.Visit(CiCondNotExpr expr)
{
expr.Inner = Coerce(Resolve(expr.Inner), CiBoolType.Value);
return(expr);
}
CiExpr ICiExprVisitor.Visit(CiPostfixExpr expr)
{
expr.Inner = Coerce(ResolveLValue(expr.Inner), CiIntType.Value);
return(expr);
}
CiExpr ICiExprVisitor.Visit(CiBinaryExpr expr)
{
CiExpr left = Resolve(expr.Left);
CiExpr right = Resolve(expr.Right);
if (expr.Op == CiToken.Plus && (left.Type is CiStringType || right.Type is CiStringType))
{
if (!(left is CiConstExpr && right is CiConstExpr))
{
throw new ResolveException("String concatenation allowed only for constants. Consider using +=");
}
string a = GetConstString(left);
string b = GetConstString(right);
return(new CiConstExpr(a + b));
}
left = Coerce(left, CiIntType.Value);
right = Coerce(right, CiIntType.Value);
if (right is CiConstExpr)
{
int b = GetConstInt(right);
if (left is CiConstExpr)
{
int a = GetConstInt(left);
switch (expr.Op)
{
case CiToken.Asterisk: a *= b; break;
case CiToken.Slash: a /= b; break;
case CiToken.Mod: a %= b; break;
case CiToken.And: a &= b; break;
case CiToken.ShiftLeft: a <<= b; break;
case CiToken.ShiftRight: a >>= b; break;
case CiToken.Plus: a += b; break;
case CiToken.Minus: a -= b; break;
case CiToken.Or: a |= b; break;
case CiToken.Xor: a ^= b; break;
}
return(new CiConstExpr(a));
}
if (expr.Op == CiToken.And && (b & ~0xff) == 0)
{
CiCoercion c = left as CiCoercion;
if (c != null && c.Inner.Type == CiByteType.Value)
{
left = (CiExpr)c.Inner;
}
}
}
expr.Left = left;
expr.Right = right;
return(expr);
}
static CiType FindCommonType(CiExpr expr1, CiExpr expr2)
{
CiType type1 = expr1.Type;
CiType type2 = expr2.Type;
if (type1.Equals(type2))
{
return(type1);
}
if ((type1 == CiIntType.Value && type2 == CiByteType.Value) ||
(type1 == CiByteType.Value && type2 == CiIntType.Value))
{
return(CiIntType.Value);
}
CiType type = type1.Ptr;
if (type != null)
{
return(type); // stg, ptr || stg, null
}
type = type2.Ptr;
if (type != null)
{
return(type); // ptr, stg || null, stg
}
if (type1 != CiType.Null)
{
return(type1); // ptr, null
}
if (type2 != CiType.Null)
{
return(type2); // null, ptr
}
throw new ResolveException("Incompatible types");
}
CiExpr ICiExprVisitor.Visit(CiBoolBinaryExpr expr)
{
CiExpr left = Resolve(expr.Left);
CiExpr right = Resolve(expr.Right);
CiType type;
switch (expr.Op)
{
case CiToken.CondAnd:
case CiToken.CondOr:
type = CiBoolType.Value;
break;
case CiToken.Equal:
case CiToken.NotEqual:
type = FindCommonType(left, right);
break;
default:
type = CiIntType.Value;
break;
}
expr.Left = Coerce(left, type);
expr.Right = Coerce(right, type);
CiConstExpr cleft = expr.Left as CiConstExpr;
if (cleft != null)
{
switch (expr.Op)
{
case CiToken.CondAnd:
return((bool)cleft.Value ? expr.Right : new CiConstExpr(false));
case CiToken.CondOr:
return((bool)cleft.Value ? new CiConstExpr(true) : expr.Right);
case CiToken.Equal:
case CiToken.NotEqual:
CiConstExpr cright = expr.Right as CiConstExpr;
if (cright != null)
{
bool eq = object.Equals(cleft.Value, cright.Value);
return(new CiConstExpr(expr.Op == CiToken.Equal ? eq : !eq));
}
break;
default:
if (expr.Right is CiConstExpr)
{
int a = GetConstInt(cleft);
int b = GetConstInt(expr.Right);
bool result;
switch (expr.Op)
{
case CiToken.Less: result = a < b; break;
case CiToken.LessOrEqual: result = a <= b; break;
case CiToken.Greater: result = a > b; break;
case CiToken.GreaterOrEqual: result = a >= b; break;
default: return(expr);
}
return(new CiConstExpr(result));
}
break;
}
}
return(expr);
}
CiExpr ICiExprVisitor.Visit(CiCondExpr expr)
{
expr.Cond = Coerce(Resolve(expr.Cond), CiBoolType.Value);
CiExpr expr1 = Resolve(expr.OnTrue);
CiExpr expr2 = Resolve(expr.OnFalse);
expr.ResultType = FindCommonType(expr1, expr2);
expr.OnTrue = Coerce(expr1, expr.ResultType);
expr.OnFalse = Coerce(expr2, expr.ResultType);
CiConstExpr konst = expr.Cond as CiConstExpr;
if (konst != null)
{
return((bool)konst.Value ? expr.OnTrue : expr.OnFalse);
}
return(expr);
}
CiExpr ICiExprVisitor.Visit(CiBinaryResourceExpr expr)
{
string name = (string)ResolveConstExpr(expr.NameExpr, CiStringPtrType.Value);
CiBinaryResource resource;
if (!this.BinaryResources.TryGetValue(name, out resource))
{
resource = new CiBinaryResource();
resource.Name = name;
resource.Content = File.ReadAllBytes(FindFile(name));
resource.Type = new CiArrayStorageType {
ElementType = CiByteType.Value, Length = resource.Content.Length
};
this.BinaryResources.Add(name, resource);
}
expr.Resource = resource;
return(expr);
}
CiExpr ICiExprVisitor.Visit(CiNewExpr expr)
{
CiType type = expr.NewType;
CiClassStorageType classStorageType = type as CiClassStorageType;
if (classStorageType != null)
{
classStorageType.Class = ResolveClass(classStorageType.Class);
classStorageType.Class.IsAllocated = true;
}
else
{
CiArrayStorageType arrayStorageType = (CiArrayStorageType)type;
arrayStorageType.ElementType = Resolve(arrayStorageType.ElementType);
arrayStorageType.LengthExpr = Coerce(Resolve(arrayStorageType.LengthExpr), CiIntType.Value);
}
return(expr);
}
CiExpr Resolve(CiExpr expr)
{
return(expr.Accept(this));
}
void ICiSymbolVisitor.Visit(CiField field)
{
field.Type = Resolve(field.Type);
}
bool Resolve(ICiStatement[] statements)
{
bool reachable = true;
foreach (ICiStatement child in statements)
{
if (!reachable)
{
throw new ResolveException("Unreachable statement");
}
child.Accept(this);
reachable = child.CompletesNormally;
}
return(reachable);
}
void ICiStatementVisitor.Visit(CiBlock statement)
{
statement.CompletesNormally = Resolve(statement.Statements);
}
void ICiStatementVisitor.Visit(CiConst statement)
{
}
void ICiStatementVisitor.Visit(CiVar statement)
{
statement.Type = Resolve(statement.Type);
if (statement.InitialValue != null)
{
CiType type = statement.Type;
CiExpr initialValue = Resolve(statement.InitialValue);
CheckCopyPtr(type, initialValue);
if (type is CiArrayStorageType)
{
type = ((CiArrayStorageType)type).ElementType;
CiConstExpr ce = Coerce(initialValue, type) as CiConstExpr;
if (ce == null)
{
throw new ResolveException("Array initializer is not constant");
}
statement.InitialValue = ce;
if (type == CiBoolType.Value)
{
if (!false.Equals(ce.Value))
{
throw new ResolveException("Bool arrays can only be initialized with false");
}
}
else if (type == CiByteType.Value)
{
if (!((byte)0).Equals(ce.Value))
{
throw new ResolveException("Byte arrays can only be initialized with zero");
}
}
else if (type == CiIntType.Value)
{
if (!0.Equals(ce.Value))
{
throw new ResolveException("Int arrays can only be initialized with zero");
}
}
else
{
throw new ResolveException("Invalid array initializer");
}
}
else
{
statement.InitialValue = Coerce(initialValue, type);
}
}
}
void ICiStatementVisitor.Visit(CiExpr statement)
{
Resolve((CiExpr)statement);
}
void ICiStatementVisitor.Visit(CiAssign statement)
{
statement.Target = ResolveLValue(statement.Target);
if (statement.Target is CiVarAccess && ((CiVarAccess)statement.Target).Var == this.CurrentMethod.This)
{
throw new ResolveException("Cannot assign to this");
}
CiMaybeAssign source = statement.Source;
if (source is CiAssign)
{
Resolve((ICiStatement)source);
}
else
{
source = Resolve((CiExpr)source);
}
CiType type = statement.Target.Type;
CheckCopyPtr(type, source);
statement.Source = Coerce(source, type);
if (statement.Op != CiToken.Assign && type != CiIntType.Value && type != CiByteType.Value)
{
if (statement.Op == CiToken.AddAssign && type is CiStringStorageType && statement.Source.Type is CiStringType)
{
} // OK
else
{
throw new ResolveException("Invalid compound assignment");
}
}
}
void ICiStatementVisitor.Visit(CiDelete statement)
{
statement.Expr = Resolve(statement.Expr);
ICiPtrType type = statement.Expr.Type as ICiPtrType;
if (type == null)
{
throw new ResolveException("'delete' takes a class or array pointer");
}
if (statement.Expr.HasSideEffect)
{
throw new ResolveException("Side effects not allowed in 'delete'");
}
this.WritablePtrTypes.Add(type);
}
void ICiStatementVisitor.Visit(CiBreak statement)
{
if (this.CurrentLoopOrSwitch == null)
{
throw new ResolveException("break outside loop and switch");
}
this.CurrentLoopOrSwitch.CompletesNormally = true;
}
void ICiStatementVisitor.Visit(CiContinue statement)
{
if (this.CurrentLoop == null)
{
throw new ResolveException("continue outside loop");
}
}
void ResolveLoop(CiLoop statement)
{
statement.CompletesNormally = false;
if (statement.Cond != null)
{
statement.Cond = Coerce(Resolve(statement.Cond), CiBoolType.Value);
statement.CompletesNormally = !statement.Cond.IsConst(false);
}
CiLoop oldLoop = this.CurrentLoop;
CiCondCompletionStatement oldLoopOrSwitch = this.CurrentLoopOrSwitch;
this.CurrentLoopOrSwitch = this.CurrentLoop = statement;
Resolve(statement.Body);
this.CurrentLoop = oldLoop;
this.CurrentLoopOrSwitch = oldLoopOrSwitch;
}
void ICiStatementVisitor.Visit(CiDoWhile statement)
{
ResolveLoop(statement);
}
void ICiStatementVisitor.Visit(CiFor statement)
{
if (statement.Init != null)
{
Resolve(statement.Init);
}
if (statement.Advance != null)
{
Resolve(statement.Advance);
}
ResolveLoop(statement);
}
void ICiStatementVisitor.Visit(CiIf statement)
{
statement.Cond = Coerce(Resolve(statement.Cond), CiBoolType.Value);
Resolve(statement.OnTrue);
if (statement.OnFalse != null)
{
Resolve(statement.OnFalse);
statement.CompletesNormally = statement.OnTrue.CompletesNormally || statement.OnFalse.CompletesNormally;
}
else
{
statement.CompletesNormally = true;
}
}
void ICiStatementVisitor.Visit(CiNativeBlock statement)
{
}
void ICiStatementVisitor.Visit(CiReturn statement)
{
CiType type = this.CurrentMethod.Signature.ReturnType;
if (type != CiType.Void)
{
statement.Value = Coerce(Resolve(statement.Value), type);
}
}
void ICiStatementVisitor.Visit(CiSwitch statement)
{
statement.Value = Resolve(statement.Value);
CiType type = statement.Value.Type;
CiCondCompletionStatement oldLoopOrSwitch = this.CurrentLoopOrSwitch;
this.CurrentLoopOrSwitch = statement;
HashSet <object> values = new HashSet <object>();
CiCase fallthroughFrom = null;
foreach (CiCase kase in statement.Cases)
{
for (int i = 0; i < kase.Values.Length; i++)
{
kase.Values[i] = ResolveConstExpr((CiExpr)kase.Values[i], type);
if (!values.Add(kase.Values[i]))
{
throw new ResolveException("Duplicate case value");
}
}
if (fallthroughFrom != null)
{
if (fallthroughFrom.FallthroughTo == null)
{
throw new ResolveException("goto default followed by case");
}
if (!ResolveConstExpr(fallthroughFrom.FallthroughTo, type).Equals(kase.Values[0]))
{
throw new ResolveException("goto case doesn't match the next case");
}
}
bool reachable = Resolve(kase.Body);
if (kase.Fallthrough)
{
if (!reachable)
{
throw new ResolveException("goto is not reachable");
}
fallthroughFrom = kase;
}
else
{
if (reachable)
{
throw new ResolveException("case must end with break, return, throw or goto");
}
fallthroughFrom = null;
}
}
if (statement.DefaultBody != null)
{
if (fallthroughFrom != null && fallthroughFrom.FallthroughTo != null)
{
throw new ResolveException("goto case followed by default");
}
bool reachable = Resolve(statement.DefaultBody);
if (reachable)
{
throw new ResolveException("default must end with break, return, throw or goto");
}
}
else
{
if (fallthroughFrom != null)
{
throw new ResolveException("goto cannot be the last statement in switch");
}
}
this.CurrentLoopOrSwitch = oldLoopOrSwitch;
}
void ICiStatementVisitor.Visit(CiThrow statement)
{
statement.Message = Coerce(Resolve(statement.Message), CiStringPtrType.Value);
this.ThrowingMethods.Add(this.CurrentMethod);
}
void ICiStatementVisitor.Visit(CiWhile statement)
{
ResolveLoop(statement);
}
void Resolve(ICiStatement statement)
{
statement.Accept(this);
}
void ICiSymbolVisitor.Visit(CiMethod method)
{
this.CurrentMethod = method;
Resolve(method.Signature);
if (method.CallType != CiCallType.Abstract)
{
Resolve(method.Body);
if (method.Signature.ReturnType != CiType.Void && method.Body.CompletesNormally)
{
throw new ResolveException("Method can complete without a return value");
}
}
this.CurrentMethod = null;
}
void ResolveBase(CiClass klass)
{
if (klass.BaseClass != null)
{
klass.BaseClass = ResolveClass(klass.BaseClass);
klass.Members.Parent = klass.BaseClass.Members;
}
}
void ICiSymbolVisitor.Visit(CiClass klass)
{
this.CurrentClass = klass;
this.Symbols = klass.Members;
if (klass.Constructor != null)
{
klass.Constructor.Accept(this);
}
foreach (CiSymbol member in klass.Members)
{
member.Accept(this);
}
klass.BinaryResources = this.BinaryResources.Values.ToArray();
this.BinaryResources.Clear();
this.Symbols = this.Symbols.Parent;
this.CurrentClass = null;
}