Example #1
0
        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");
        }
Example #2
0
        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;
        }