public void SetCilHintTest() { Compile(@" function main() { foreach(var x in var args) println(x); }"); var main = target.Functions["main"]; var cilExt1 = new CilExtensionHint(new List<int> {1, 5, 9}); var existingHints = _getCilHints(main, true); Assert.AreEqual(1, existingHints.Length); //Add, none existing Compiler.SetCilHint(main, cilExt1); var hints1 = _getCilHints(main, true); Assert.AreNotSame(existingHints, hints1); Assert.AreEqual(2, hints1.Length); Assert.IsTrue(hints1[1].IsList); var cilExt1P = CilExtensionHint.FromMetaEntry(hints1[1].List); Assert.IsTrue( cilExt1P.Offsets.All(offset => cilExt1.Offsets.Contains(offset)), "deserialized contains elements not in original"); Assert.IsTrue(cilExt1.Offsets.All(offset => cilExt1P.Offsets.Contains(offset)), "original contains elements not in deserialized"); //Add, one existing var cilExt2 = new CilExtensionHint(new List<int> {2, 4, 8, 16}); Compiler.SetCilHint(main, cilExt2); var hints2 = _getCilHints(main, true); Assert.AreSame(hints1, hints2); Assert.AreEqual(2, hints2.Length); Assert.IsTrue(hints2[1].IsList); var cilExt2P = CilExtensionHint.FromMetaEntry(hints2[1].List); Assert.IsTrue( cilExt2P.Offsets.All(offset => cilExt2.Offsets.Contains(offset)), "deserialized contains elements not in original"); Assert.IsTrue(cilExt2.Offsets.All(offset => cilExt2P.Offsets.Contains(offset)), "original contains elements not in deserialized"); //Add, many existing var cilExts = new List<CilExtensionHint> { new CilExtensionHint(new List<int> {1, 6, 16, 66}), new CilExtensionHint(new List<int> {7, 77, 777}), new CilExtensionHint(new List<int> {9, 88, 777, 6666}), }; foreach (var cilExt in cilExts) Compiler.AddCilHint(main, cilExt); var hints3 = _getCilHints(main, true); Assert.AreNotSame(hints2, hints3); Assert.AreEqual(5, hints3.Length); var cilExt3 = new CilExtensionHint(new List<int> {44, 55, 66, 77, 88}); Compiler.SetCilHint(main, cilExt3); var hints4 = _getCilHints(main, true); Assert.AreNotSame(hints3, hints4); Assert.AreEqual(2, hints4.Length); Assert.IsTrue(hints4[1].IsList); var cilExt3P = CilExtensionHint.FromMetaEntry(hints4[1].List); Assert.IsTrue( cilExt3P.Offsets.All(offset => cilExt3.Offsets.Contains(offset)), "deserialized contains elements not in original"); Assert.IsTrue(cilExt3.Offsets.All(offset => cilExt3P.Offsets.Contains(offset)), "original contains elements not in deserialized"); //Add, no cil hints key yet var emptyFunc = target.CreateFunction(); emptyFunc.Meta[PFunction.IdKey] = "empty"; Compiler.SetCilHint(main, cilExt3); var hints5 = _getCilHints(main, true); Assert.AreEqual(2, hints5.Length); Assert.IsTrue(hints5[0].IsList); var cilExt4P = CilExtensionHint.FromMetaEntry(hints5[1].List); Assert.IsTrue( cilExt4P.Offsets.All(offset => cilExt3.Offsets.Contains(offset)), "deserialized contains elements not in original"); Assert.IsTrue(cilExt3.Offsets.All(offset => cilExt4P.Offsets.Contains(offset)), "original contains elements not in deserialized"); }
private static bool _check(PFunction source, Engine targetEngine, out string reason) { if (source == null) throw new ArgumentNullException("source"); if (targetEngine == null) throw new ArgumentNullException("targetEngine"); //Application does not allow cil compilation if ((!source.Meta.ContainsKey(PFunction.VolatileKey)) && source.ParentApplication.Meta[PFunction.VolatileKey].Switch) { reason = "Application does not allow cil compilation"; return false; } //Function does not allow cil compilation if (source.Meta[PFunction.VolatileKey].Switch) { reason = null; //don't add a message return false; } //Prepare for CIL extensions var cilExtensions = new List<int>(); var localVariableMapping = new Dictionary<int, string>(source.LocalVariableMapping.Count); foreach (var kvp in source.LocalVariableMapping) localVariableMapping[kvp.Value] = kvp.Key; var jumpTargets = new HashSet<int>(from ins in source.Code where ins.OpCode == OpCode.jump || ins.OpCode == OpCode.jump_t || ins.OpCode == OpCode.jump_f select ins.Arguments); var seh = new StructuredExceptionHandling(source); //Check for not supported instructions and instructions used in a way // that is not supported by the CIL compiler) for (var insOffset = 0; insOffset < source.Code.Count; insOffset++) { var address = insOffset; var ins = source.Code[address]; switch (ins.OpCode) { case OpCode.cmd: //Check for commands that are not compatible. ICommandInfo cmd; if (!targetEngine.Commands.TryGetInfo(ins.Id, out cmd)) { reason = "Cannot find information about command " + ins.Id; return false; } ICilExtension extension; ICilCompilerAware aware; CompileTimeValue[] staticArgv; //First allow CIL extensions to kick in, and only if they don't apply, check for CIL awareness. if (cmd.TryGetCilExtension(out extension) && !_rangeInSet( insOffset - (staticArgv = CompileTimeValue.ParseSequenceReverse(source.Code, localVariableMapping, address - 1, source.ParentApplication.Module.Cache,source.ParentApplication.Module.Name)).Length + 1, staticArgv.Length, jumpTargets) && extension.ValidateArguments(staticArgv, ins.Arguments - staticArgv.Length)) { cilExtensions.Add(address - staticArgv.Length); } else if (cmd.TryGetCilCompilerAware(out aware)) { var flags = aware.CheckQualification(ins); if (flags == CompilationFlags.IsIncompatible) //Incompatible and no workaround { reason = "Incompatible command " + ins.Id; return false; } } break; case OpCode.func: //Check for functions that use dynamic features PFunction func; if (source.ParentApplication.Functions.TryGetValue(ins.Id, out func) && func.Meta[PFunction.DynamicKey].Switch) { reason = "Uses dynamic function " + ins.Id; return false; } break; case OpCode.tail: case OpCode.invalid: reason = "Unsupported instruction " + ins; return false; case OpCode.newclo: //Function must already be available if (!source.ParentApplication.Functions.Contains(ins.Id)) { reason = "Enclosed function " + ins.Id + " must already be compiled (closure creation)"; return false; } break; case OpCode.@try: //must be the first instruction of a try block var isCorrect = source.TryCatchFinallyBlocks.Any(block => block.BeginTry == address); if (!isCorrect) { reason = "try instruction is not the first instruction of a guarded block."; return false; } break; case OpCode.exc: //must be the first instruction of a catch block isCorrect = source.TryCatchFinallyBlocks.Any(block => block.BeginCatch == address); if (!isCorrect) { reason = "exc instruction is not the first instruction of a catch clause."; return false; } break; case OpCode.jump: case OpCode.jump_t: case OpCode.jump_f: case OpCode.leave: if (seh.AssessJump(address, ins.Arguments) == BranchHandling.Invalid) { reason = "jumping instruction at " + address + " invalid for SEH"; return false; } break; case OpCode.ret_break: case OpCode.ret_continue: case OpCode.ret_exit: case OpCode.ret_value: if (seh.AssessJump(address, source.Code.Count) == BranchHandling.Invalid) { reason = "return instruction at " + address + " invalid for SEH"; return false; } break; } } if (cilExtensions.Count > 0) { var cilExtensionHint = new CilExtensionHint(cilExtensions); SetCilHint(source, cilExtensionHint); } //Otherwise, qualification passed. reason = null; return true; }