internal void AddFlagsAndData(string name, ElaVariableFlags flags, int data) { var sv = Locals[name]; sv = new ScopeVar(sv.Flags | flags, sv.Address, data); Locals[name] = sv; }
internal void RemoveFlags(string name, ElaVariableFlags flags) { var sv = Locals[name]; sv = new ScopeVar(sv.Flags ^ flags, sv.Address, sv.Data); Locals[name] = sv; }
//This method should be used always instead of direct emitting Pushvar/Pushloc op codes. //It first checks if a given variable is an external and in such a case generates //a different op typeId. For addr it uses PushVar(int) to generate an appropriate op typeId. private void PushVar(ScopeVar sv) { if ((sv.Flags & ElaVariableFlags.External) == ElaVariableFlags.External) { cw.Emit(Op.Pushext, sv.Address); } else { PushVar(sv.Address); } }
internal int TryChangeVariable(string name) { var v = default(ScopeVar); if (Locals.TryGetValue(name, out v)) { v = new ScopeVar(ElaVariableFlags.None, v.Address, -1); Locals[name] = v; return(v.Address); } return(-1); }
//Checks if a field reference is actually an exported name prefixed by a module //and if so emits a direct Pushexit op typeId. If this is not the case returns false //and the typeId gets compiled as a regular field reference. private bool TryOptimizeFieldReference(ElaFieldReference p, out ScopeVar fieldSv) { fieldSv = default(ScopeVar); if (p.TargetObject.Type != ElaNodeType.NameReference) { return(false); } var mod = default(ModuleReference); var sv = GetVariable(p.TargetObject.GetName(), p.TargetObject.Line, p.TargetObject.Column); //Looks like a target object is not a module if ((sv.Flags & ElaVariableFlags.Module) != ElaVariableFlags.Module || !frame.References.TryGetValue(p.TargetObject.GetName(), out mod)) { return(false); } //We have such a reference but looks like it couldn't be obtained //We don't need to handle this situation here, it is already reported by a linker if (refs[mod.LogicalHandle] == null) { return(false); } //No such name, now captured statically if (!refs[mod.LogicalHandle].GlobalScope.Locals.TryGetValue(p.FieldName, out fieldSv) && !options.IgnoreUndefined) { AddError(ElaCompilerError.UndefinedNameInModule, p, p.TargetObject.GetName(), p.FieldName); return(false); } //Name is private, now captured statically if ((fieldSv.VariableFlags & ElaVariableFlags.Private) == ElaVariableFlags.Private) { AddError(ElaCompilerError.PrivateNameInModule, p, p.FieldName, p.TargetObject.GetName()); return(false); } AddLinePragma(p); cw.Emit(Op.Pushext, mod.LogicalHandle | fieldSv.Address << 8); return(true); }
//Looks for a 'table' function (a class function to which we would add instances). //It can be optionally qualified with a 'classPrefix' (which is a module alias). private ScopeVar ObtainClassFunction(string classPrefix, string className, string name, int line, int col) { var btVar = default(ScopeVar); if (classPrefix == null && !frame.InternalClasses.ContainsKey(className)) { //We can't look for this name directly, because it can be shadowed. //First, we need to obtain a class reference, which is used to obtain a local //module ID (encoded in address). Then we directly look for this name in the module. var cv = GetGlobalVariable("$$$" + className, GetFlags.NoError, line, col); var moduleHandle = cv.Address & Byte.MaxValue; if (moduleHandle < 0 || moduleHandle >= refs.Count || !refs[moduleHandle].GlobalScope.Locals.TryGetValue(name, out btVar)) { btVar = ScopeVar.Empty; } else { //OK, found, but now we need to patch this variable, so it will be correctly //encoded as an external name. btVar = new ScopeVar(btVar.Flags | ElaVariableFlags.External, moduleHandle | btVar.Address << 8, btVar.Data); } } else if (classPrefix == null) { btVar = GetVariable(name, CurrentScope, GetFlags.NoError, line, col); } else { CodeFrame _; btVar = FindByPrefix(classPrefix, name, out _); } return(btVar); }
//Some members may be generated directly by compiler. Here we check if this is the case for this //particular member. private bool TryAutogenerate(ScopeVar var, ElaClassInstance inst) { if ((var.Flags & ElaVariableFlags.Builtin) != ElaVariableFlags.Builtin) { return(false); } switch ((ElaBuiltinKind)var.Data) { //Used to generate Bounded.maxBound constant case ElaBuiltinKind.GenMaxBound: cw.Emit(Op.PushI4, 1); //Obtain type ID, no errors, they are captured elsewhere EmitSpecName(inst.TypePrefix, "$$" + inst.TypeName, inst, ElaCompilerError.None); cw.Emit(Op.Api, (Int32)Api.TypeConsNumber); cw.Emit(Op.Sub); EmitSpecName(inst.TypePrefix, "$$" + inst.TypeName, inst, ElaCompilerError.None); cw.Emit(Op.Api2, (Int32)Api.ConsCodeByIndex); cw.Emit(Op.Api, (Int32)Api.ConsDefault); return(true); //Used to generate Bounded.minBound constant case ElaBuiltinKind.GenMinBound: case ElaBuiltinKind.GenDefault: cw.Emit(Op.PushI4_0); //Obtain type ID, no errors, they are captured elsewhere EmitSpecName(inst.TypePrefix, "$$" + inst.TypeName, inst, ElaCompilerError.None); cw.Emit(Op.Api2, (Int32)Api.ConsCodeByIndex); cw.Emit(Op.Api, (Int32)Api.ConsDefault); return(true); default: return(false); } }
//Inlines a constructor call, method is called from TryOptimizeConstructor private void CompileConstructorCall(string prefix, string name, ElaJuxtaposition juxta, LabelMap map, ScopeVar sv, ScopeVar sv2) { var len = juxta.Parameters.Count; //For optimization purposes we use a simplified creation algorythm for constructors //with 1 and 2 parameters if (len == 1) { CompileExpression(juxta.Parameters[0], map, Hints.None, juxta); TypeCheckIf(prefix, name, 0); } else if (len == 2) { CompileExpression(juxta.Parameters[0], map, Hints.None, juxta); TypeCheckIf(prefix, name, 0); CompileExpression(juxta.Parameters[1], map, Hints.None, juxta); TypeCheckIf(prefix, name, 1); } else { CompileConstructorParameters(prefix, name, juxta, map); } PushVar(sv); PushVar(sv2); if (len == 1) { cw.Emit(Op.Newtype1); } else if (len == 2) { cw.Emit(Op.Newtype2); } else { cw.Emit(Op.Newtype); } }
//This method is used to generate Push* op typeId for a special name prefixed by $$ or $$$. //Such names are used for hidden variables that contains unique codes of types and classes. //This method can emit a qualified name (with a module prefix) as well as a short name. //It also generates an appropriate error if a name is not found. //Specifying ElaCompilerError.None would force this method to not generate any error messages. private ScopeVar EmitSpecName(string ns, string specName, ElaExpression exp, ElaCompilerError err, out int modId) { if (ns != null) { var v = GetVariable(ns, exp.Line, exp.Column); //A prefix (ns) is not a module alias which is an error if ((v.Flags & ElaVariableFlags.Module) != ElaVariableFlags.Module && err != ElaCompilerError.None) { AddError(ElaCompilerError.InvalidQualident, exp, ns); } ModuleReference mr; var lh = -1; if (frame.References.TryGetValue(ns, out mr)) { lh = mr.LogicalHandle; } var extVar = default(ScopeVar); var mod = lh > -1 && lh < refs.Count ? refs[lh] : null; //A name (or even module) not found, generate an error if ((mod == null || !mod.GlobalScope.Locals.TryGetValue(specName, out extVar)) && !options.IgnoreUndefined && err != ElaCompilerError.None) { //No need to apped alias if we want to generate UndefinedName. That would be misleading. if (err == ElaCompilerError.UndefinedName) { AddError(err, exp, specName.TrimStart('$')); } else { AddError(err, exp, ns + "." + specName.TrimStart('$')); } } if ((extVar.Flags & ElaVariableFlags.Private) == ElaVariableFlags.Private && err != ElaCompilerError.None) { AddError(ElaCompilerError.PrivateNameInModule, exp, specName.TrimStart('$'), ns); } modId = lh; extVar = new ScopeVar(extVar.Flags | ElaVariableFlags.External, lh | (extVar.Address << 8), extVar.Data); PushVar(extVar); return(extVar); } else { //Without a qualident it is pretty straightforward var a = GetVariable(specName, CurrentScope, GetFlags.NoError, exp.Line, exp.Column); if (a.IsEmpty() && !options.IgnoreUndefined && err != ElaCompilerError.None) { AddError(err, exp, specName.TrimStart('$')); } modId = (a.Flags & ElaVariableFlags.External) == ElaVariableFlags.External ? a.Address & Byte.MaxValue : -1; PushVar(a); return(a); } }
//Compiles a type constructor private void CompileConstructorFunction(string typeName, string name, ElaJuxtaposition juxta, int sca, ElaVariableFlags flags, int typeModuleId) { Label funSkipLabel; int address; LabelMap newMap; var pars = new List <String>(); var len = juxta.Parameters.Count; AddLinePragma(juxta); CompileFunctionProlog(name, len, juxta.Line, juxta.Column, out funSkipLabel, out address, out newMap); var sys = new int[len]; var types = new ScopeVar[len]; var bangs = new bool[len]; //Here we have to validate all constructor parameters for (var i = 0; i < len; i++) { var ce = juxta.Parameters[i]; sys[i] = AddVariable(); if (bangs[i] = IsBang(ce)) { cw.Emit(Op.Force); } PopVar(sys[i]); //This can be type a type constraint so we should compile here type check logic if (ce.Type == ElaNodeType.Juxtaposition) { var jc = (ElaJuxtaposition)ce; //First we check if a constraint is actually valid if (IsInvalidConstructorParameterConstaint(jc)) { AddError(ElaCompilerError.InvalidConstructorParameter, juxta, FormatNode(ce), name); } else if (jc.Target.Type == ElaNodeType.NameReference) { //A simple direct type reference var nt = (ElaNameReference)jc.Target; PushVar(sys[i]); types[i] = TypeCheckConstructor(name, null, nt.Name, nt, false); pars.Add(jc.Parameters[0].GetName()); } else if (jc.Target.Type == ElaNodeType.FieldReference) { //A type is qualified with a module alias var fr = (ElaFieldReference)jc.Target; PushVar(sys[i]); types[i] = TypeCheckConstructor(name, fr.TargetObject.GetName(), fr.FieldName, fr, false); pars.Add(jc.Parameters[0].GetName()); } } else if (ce.Type == ElaNodeType.NameReference && !IsInvalidConstructorParameter(ce)) { pars.Add(ce.GetName()); types[i] = ScopeVar.Empty; } else { AddError(ElaCompilerError.InvalidConstructorParameter, juxta, FormatNode(ce), name); } } frame.InternalConstructors.Add(new ConstructorData { TypeName = typeName, Name = name, Code = -1, Parameters = pars, TypeModuleId = typeModuleId }); //For optimization purposes we use a simplified creation algorythm for constructors //with 1 and 2 parameters if (len == 1) { PushVar(sys[0]); } else if (len == 2) { PushVar(sys[0]); PushVar(sys[1]); } else { cw.Emit(Op.Newtup, len); for (var i = 0; i < len; i++) { PushVar(sys[i]); cw.Emit(Op.Tupcons, i); } } var ctid = frame.InternalConstructors.Count - 1; cw.Emit(Op.Ctorid, ctid); //Refering to captured name, need to recode its address PushVar((Byte.MaxValue & sca) + 1 | (sca << 8) >> 8); if (len == 1) { cw.Emit(Op.Newtype1); } else if (len == 2) { cw.Emit(Op.Newtype2); } else { cw.Emit(Op.Newtype); } CompileFunctionEpilog(name, len, address, funSkipLabel); var a = AddVariable(name, juxta, ElaVariableFlags.TypeFun | ElaVariableFlags.Function | flags, len); PopVar(a); //We need to add special variable that would indicate that a constructor parameter //should be strictly evaluated. Used when inlining constructors. This variable is for //metadata only, it is never initialized. for (var i = 0; i < bangs.Length; i++) { if (bangs[i]) { CurrentScope.Locals.Remove("$-!" + i + name); //To prevent redundant errors AddVariable("$-!" + i + name, juxta, ElaVariableFlags.CompilerGenerated, -1); } } //We need to add special variable that would store type check information. //This information is used when inlining constructors. for (var i = 0; i < types.Length; i++) { var sv = types[i]; //There is a type constraint, we have to memoize it for inlining if (!sv.IsEmpty()) { CurrentScope.Locals.Remove("$-" + i + name); //To prevent redundant errors var av = AddVariable("$-" + i + name, juxta, ElaVariableFlags.CompilerGenerated, -1); //This ScopeVar was obtained in a different scope, we have to patch it here if ((sv.Flags & ElaVariableFlags.External) == ElaVariableFlags.External) { PushVar(sv); //No need to patch an external } else { //Roll up one scope sv.Address = ((sv.Address & Byte.MaxValue) - 1) | (sv.Address >> 8) << 8; PushVar(sv); } PopVar(av); } } //To prevent redundant errors CurrentScope.Locals.Remove("$-" + name); CurrentScope.Locals.Remove("$--" + name); //We add special variables that can be used lately to inline this constructor call. var consVar = AddVariable("$-" + name, juxta, flags | ElaVariableFlags.CompilerGenerated, len); var typeVar = AddVariable("$--" + name, juxta, flags | ElaVariableFlags.CompilerGenerated, len); cw.Emit(Op.Ctorid, ctid); PopVar(consVar); PushVar(sca); PopVar(typeVar); }