/* Todo, perhaps: Add function which allows C# to add static functions later. Could help C#-Pebble communication. * public Variable AddStaticLiteralAfterFinalization(ExecContext context, string name, ITypeDef typeDef, object value) { * Variable variable = new Variable(name, typeDef); * staticVars.Add(variable); * variable.value = value; * return variable; * } */ // Use to add an override of a member function. // Caller is responsible for checking that... // 1) we have a parent // 2) it has a function with this name. public void AddFunctionOverride(string name, ITypeDef typeDef, IExpr initializer = null) { var member = new ClassMember(name, typeDef, initializer, false); //var mr = GetMemberRef(name); member.index = _memberFuncs.Set(name, member); }
// Returns true if it succeeds, false if there was a name conflict. public bool AddMember(string name, ITypeDef typeDef, IExpr initializer = null, bool isStatic = false, bool isFunctionVariable = false, bool isGetonly = false) { // Fail if a member with that name already exists. if (DoesFieldExist(name, SEARCH.EITHER)) { return(false); } var member = new ClassMember(name, typeDef, initializer, isGetonly); // This logic is weird but works because the language currently // doesn't allow static member functions. if (isStatic) { member.index = _statics.Add(member.name, member); staticVars.Add(new Variable(member.name, typeDef, null)); } else if (!isFunctionVariable && typeDef is TypeDef_Function) { member.index = _memberFuncs.Add(member.name, member); } else { member.index = _fields.Add(member.name, member); } return(true); }
public string GetDebugString() { string result = "class " + name + ":\n"; for (int ii = 0; ii < _statics.Count; ++ii) { ClassMember member = _statics.Get(ii); result += " static "; if (member.typeDef is TypeDef_Function) { result += ((TypeDef_Function)member.typeDef).GetDebugString(member.name); } else { result += member.typeDef.ToString() + " " + member.name + " = " + (null == staticVars[ii].value ? "null" : staticVars[ii].value) + ";\n"; } } for (int ii = 0; ii < _fields.Count; ++ii) { ClassMember member = _fields.Get(ii); result += " " + member.typeDef.ToString() + " " + member.name + ";\n"; } for (int ii = 0; ii < _memberFuncs.Count; ++ii) { ClassMember member = _memberFuncs.Get(ii); result += " " + ((TypeDef_Function)member.typeDef).GetDebugString(member.name); } return(result); }
// Call this after creating the class and adding members to it. public bool FinalizeClass(ExecContext context) { Pb.Assert(0 == context.control.flags); // Only initializing the members that this class has added is a cool idea, but // leaves us f****d when it comes to overrridden functions. // So, I'm being lazy here and initializing them all. for (int ii = 0; ii < _memberFuncs.Count; ++ii) { ClassMember member = _memberFuncs.Get(ii); if (null != member.initializer && (member.initializer is Expr_Literal || member.initializer is Expr_Value)) { // Make sure vftableVars has a slot for this function. while (vftableVars.Count < ii + 1) { vftableVars.Add(null); } object initValue = member.initializer.Evaluate(context); if (context.IsRuntimeErrorSet()) { return(false); } // Create the variable for the function. vftableVars[ii] = new Variable(member.name, member.typeDef, initValue); } } // Initialize the static members. Populates the staticVars list. for (int ii = 0; ii < _statics.Count; ++ii) { ClassMember member = _statics.Get(ii); object initValue = null; if (null != member.initializer) { initValue = member.initializer.Evaluate(context); if (context.IsRuntimeErrorSet()) { context.engine.LogCompileError(ParseErrorType.StaticMemberEvaluationError, name + "::" + member.name + " - " + context.GetRuntimeErrorString()); return(false); } } staticVars[ii] = new Variable(member.name, member.typeDef, initValue); } return(true); }
// Only called by the context. public void Initialize() { if (null != _fields) { return; } // 1) fields. _fields = new MemberList(); if (null != parent) { for (int ii = 0; ii < parent._fields.Count; ++ii) { ClassMember member = parent._fields.Get(ii); bool modified = false; ITypeDef resolvedType = parent.IsGeneric() ? member.typeDef.ResolveTemplateTypes(typeDef.genericTypes, ref modified) : member.typeDef; ClassMember newMember = new ClassMember(member.name, resolvedType, member.initializer, member.isGetonly); newMember.index = _fields.Add(member.name, newMember); } } // 2) Functions _memberFuncs = new MemberList(); if (null != parent) { for (int ii = 0; ii < parent._memberFuncs.Count; ++ii) { ClassMember member = parent._memberFuncs.Get(ii); bool modified = false; ITypeDef resolvedType = parent.IsGeneric() ? member.typeDef.ResolveTemplateTypes(typeDef.genericTypes, ref modified) : member.typeDef; ClassMember newMember = new ClassMember(member.name, resolvedType, member.initializer, member.isGetonly); newMember.index = _memberFuncs.Add(member.name, newMember); } vftableVars = new List <Variable>(parent.vftableVars); } else { vftableVars = new List <Variable>(); } // 3) Statics aren't copied. _statics = new MemberList(); staticVars = new List <Variable>(); }
// Create a new instance of the class. public ClassValue Allocate(ExecContext context) { ClassValue result = childAllocator(); result.classDef = this; result.debugName = name + " Inst"; bool scopePushed = false; for (int ii = 0; ii < _fields.Count; ++ii) { ClassMember member = _fields.Get(ii); object value = null; if (null != member.initializer) { if (!scopePushed) { scopePushed = true; if (!context.stack.PushClassScope(result, context)) { context.SetRuntimeError(RuntimeErrorType.StackOverflow, "ClassValue.Allocate - stack overflow."); return(null); } } value = member.initializer.Evaluate(context); } else if (!member.typeDef.IsReference()) // don't instantiate class refs automatically { value = member.typeDef.GetDefaultValue(context); } Variable newVar = new Variable(member.name, member.typeDef, value); result.fieldVars.Add(newVar); } if (null != constructor) { if (context.IsRuntimeErrorSet()) { return(null); } if (!scopePushed) { scopePushed = true; if (!context.stack.PushClassScope(result, context)) { context.SetRuntimeError(RuntimeErrorType.StackOverflow, "ClassValue.Allocate - stack overflow."); return(null); } } constructor.Evaluate(context); if (context.IsRuntimeErrorSet()) { return(null); } } if (scopePushed) { context.stack.PopScope(); } return(result); }
// Compile-time lookup of MemberRef. public MemberRef GetMemberRef(ExecContext context, string name, SEARCH searchType, ref ITypeDef typeDef) { // If there is an error during compliation then we can have an uninitialized // class on the stack. This check prevents us from throwing an exception // when that happens. // Kind of a kludge but handling errors during compilation is pretty chaotic // and I don't know what I could do other than to just abort on the first error. if (null == _fields) { return(MemberRef.invalid); } if (SEARCH.NORMAL == searchType || SEARCH.EITHER == searchType) { if (_fields.Exists(name)) { var member = _fields.Get(name); typeDef = member.typeDef; // If this member is getonly and we are not in this class's context, the type becomes const. if (null != context && member.isGetonly) { ClassDef current = context.stack.GetCurrentClassDef(); if (null == current || (current != this && !current.IsChildOf(this))) { typeDef = TypeFactory.GetConstVersion(typeDef); } } return(new MemberRef(member.index)); } if (_memberFuncs.Exists(name)) { var member = _memberFuncs.Get(name); typeDef = member.typeDef; return(new MemberRef(this, MemberType.FUNCTION, member.index)); } } if (SEARCH.STATIC == searchType || SEARCH.EITHER == searchType) { ClassDef def = this; while (null != def) { if (def._statics.Exists(name)) { ClassMember member = def._statics.Get(name); typeDef = member.typeDef; // If this member is getonly and we are not in this class's context, the type becomes const. if (null != context && member.isGetonly) { ClassDef current = context.stack.GetCurrentClassDef(); if (null == current || (current != this && !current.IsChildOf(this))) { typeDef = TypeFactory.GetConstVersion(typeDef); } } return(new MemberRef(def, MemberType.STATIC, member.index)); } def = def.parent; } } return(MemberRef.invalid); }