示例#1
0
        protected IEnumerable <Opcode> BuildBoilerplateLoader()
        {
            List <Opcode> boilerplate = new List <Opcode>();

            InternalPath path = new BuiltInPath();

            // First label of the load/runner will be called "@LR00", which is important because that's
            // what we hardcode all the compiler-built VisitRunStatement's to call out to:
            labelCounter = 0;
            labelFormat  = "@LR{0:D2}";
            boilerplate.Add(new OpcodePushScope(-999, 0)
            {
                Label = nextLabel, SourcePath = path
            });

            // High level kerboscript function calls flip the argument orders for us, but
            // low level kRISC does not so the parameters have to be read in stack order:

            // store parameter 2 in a local name:
            boilerplate.Add(new OpcodeStoreLocal("$runonce")
            {
                Label = nextLabel, SourcePath = path
            });

            // store parameter 1 in a local name:
            boilerplate.Add(new OpcodeStoreLocal("$filename")
            {
                Label = nextLabel, SourcePath = path
            });

            // Unconditionally call load() no matter what.  load() will abort and return
            // early if the program was already compiled, and tell us that on the stack:
            boilerplate.Add(new OpcodePush(new kOS.Safe.Execution.KOSArgMarkerType())
            {
                Label = nextLabel, SourcePath = path
            });
            boilerplate.Add(new OpcodePush("$filename")
            {
                Label = nextLabel, SourcePath = path
            });
            boilerplate.Add(new OpcodeEval()
            {
                Label = nextLabel, SourcePath = path
            });
            boilerplate.Add(new OpcodePush(true)
            {
                Label = nextLabel, SourcePath = path
            });                                                                           // the flag that tells load() to abort early if it's already loaded:
            boilerplate.Add(new OpcodePush(null)
            {
                Label = nextLabel, SourcePath = path
            });
            boilerplate.Add(new OpcodeCall("load()")
            {
                Label = nextLabel, SourcePath = path
            });

            // Stack now has the 2 return values of load():
            //    Topmost value is a boolean flag for whether or not the program was already loaded.
            //    Second-from-top value is the entry point into the program.

            // If load() didn't claim the program was already loaded, or if we aren't operating
            // in "once" mode, then jump to the part where we call the loaded program, else
            // fall through to a dummy do-nothing return for the "run once, but it already ran" case:
            Opcode branchFromOne = new OpcodeBranchIfFalse()
            {
                Label = nextLabel, SourcePath = path
            };

            boilerplate.Add(branchFromOne);
            boilerplate.Add(new OpcodePush("$runonce")
            {
                Label = nextLabel, SourcePath = path
            });
            Opcode branchFromTwo = new OpcodeBranchIfFalse()
            {
                Label = nextLabel, SourcePath = path
            };

            boilerplate.Add(branchFromTwo);
            boilerplate.Add(new OpcodePop()
            {
                Label = nextLabel, SourcePath = path
            });                                                                      // onsume the entry point that load() returned. We won't be calling it.
            boilerplate.Add(new OpcodePush(0)
            {
                Label = nextLabel, SourcePath = path
            });                                                                          // ---+-- The dummy do-nothing return.
            boilerplate.Add(new OpcodeReturn(1)
            {
                Label = nextLabel, SourcePath = path
            });                                                                          // ---'

            // Actually call the Program from its entry Point, which is now the thing left on top
            // of the stack from the second return value of load():
            Opcode branchTo = new OpcodeStoreLocal("$entrypoint")
            {
                Label = nextLabel, SourcePath = path
            };

            boilerplate.Add(branchTo);
            boilerplate.Add(new OpcodeCall("$entrypoint")
            {
                Label = nextLabel, SourcePath = path
            });

            boilerplate.Add(new OpcodePop()
            {
                Label = nextLabel, SourcePath = path
            });
            boilerplate.Add(new OpcodePush(0)
            {
                Label = nextLabel, SourcePath = path
            });
            boilerplate.Add(new OpcodeReturn(1)
            {
                Label = nextLabel, SourcePath = path
            });

            branchFromOne.DestinationLabel = branchTo.Label;
            branchFromTwo.DestinationLabel = branchTo.Label;

            return(boilerplate);
        }
示例#2
0
        protected IEnumerable <Opcode> BuildBoilerplateLoader()
        {
            List <Opcode> boilerplate = new List <Opcode>();

            InternalPath path = new BuiltInPath();

            // First label of the load/runner will be called "@LR00", which is important because that's
            // what we hardcode all the compiler-built VisitRunStatement's to call out to:
            labelCounter = 0;
            labelFormat  = "@LR{0:D2}";
            boilerplate.Add(new OpcodePushScope(-999, 0)
            {
                Label = nextLabel, SourcePath = path
            });

            // High level kerboscript function calls flip the argument orders for us, but
            // low level kRISC does not so the parameters have to be read in stack order:

            // store parameter 2 in a local name:
            boilerplate.Add(new OpcodeStoreLocal("$runonce")
            {
                Label = nextLabel, SourcePath = path
            });

            // store parameter 1 in a local name:
            boilerplate.Add(new OpcodeStoreLocal("$filename")
            {
                Label = nextLabel, SourcePath = path
            });

            // Unconditionally call load() no matter what.  load() will abort and return
            // early if the program was already compiled, and tell us that on the stack:
            boilerplate.Add(new OpcodePush(new kOS.Safe.Execution.KOSArgMarkerType())
            {
                Label = nextLabel, SourcePath = path
            });
            boilerplate.Add(new OpcodePush("$filename")
            {
                Label = nextLabel, SourcePath = path
            });
            boilerplate.Add(new OpcodeEval()
            {
                Label = nextLabel, SourcePath = path
            });
            boilerplate.Add(new OpcodePush(true)
            {
                Label = nextLabel, SourcePath = path
            });                                                                           // the flag that tells load() to abort early if it's already loaded:
            boilerplate.Add(new OpcodePush(null)
            {
                Label = nextLabel, SourcePath = path
            });
            boilerplate.Add(new OpcodeCall("load()")
            {
                Label = nextLabel, SourcePath = path
            });

            // Stack now has the 2 return values of load():
            //    Topmost value is a boolean flag for whether or not the program was already loaded.
            //    Second-from-top value is the entry point into the program.

            // If load() didn't claim the program was already loaded, or if we aren't operating
            // in "once" mode, then jump to the part where we call the loaded program, else
            // fall through to a dummy do-nothing return for the "run once, but it already ran" case:
            Opcode branchFromOne = new OpcodeBranchIfFalse()
            {
                Label = nextLabel, SourcePath = path
            };

            boilerplate.Add(branchFromOne);
            boilerplate.Add(new OpcodePush("$runonce")
            {
                Label = nextLabel, SourcePath = path
            });
            Opcode branchFromTwo = new OpcodeBranchIfFalse()
            {
                Label = nextLabel, SourcePath = path
            };

            boilerplate.Add(branchFromTwo);

            // If we fall through to this opcode (the br.false above doesn't jump), we are
            // in the "program already ran, so skip running it" clause of a RUN ONCE.
            // In that case the stack will still have the jump address returned by load(),
            // and also all the args that were meant to be passed to the program (which is not
            // getting run.)  In that case, we need to pop the stack of everything above the
            // next KOSArgMarker to emulate what a call would have done to the stack had it run.
            Opcode loopStart = new OpcodePop()
            {
                Label = nextLabel, SourcePath = path
            };

            boilerplate.Add(loopStart);
            boilerplate.Add(new OpcodeTestArgBottom()
            {
                Label = nextLabel, SourcePath = path
            });
            Opcode branchThatExitsLoop = new OpcodeBranchIfTrue()
            {
                Label = nextLabel, SourcePath = path
            };

            boilerplate.Add(branchThatExitsLoop);
            boilerplate.Add(new OpcodeBranchJump()
            {
                Label = nextLabel, SourcePath = path, DestinationLabel = loopStart.Label
            });
            Opcode afterLoop = new OpcodePop()
            {
                Label = nextLabel, SourcePath = path
            };                                                                           // Pop the KOSArgMarker that was under the args

            boilerplate.Add(afterLoop);
            branchThatExitsLoop.DestinationLabel = afterLoop.Label;
            boilerplate.Add(new OpcodePush(0)
            {
                Label = nextLabel, SourcePath = path
            });                                                                            // ---+-- The dummy do-nothing return.
            boilerplate.Add(new OpcodeReturn(1)
            {
                Label = nextLabel, SourcePath = path
            });                                                                          // ---'

            // Actually call the Program from its entry Point, which is now the thing left on top
            // of the stack from the second return value of load():
            Opcode branchTo = new OpcodeStoreLocal("$entrypoint")
            {
                Label = nextLabel, SourcePath = path
            };

            boilerplate.Add(branchTo);
            boilerplate.Add(new OpcodeCall("$entrypoint")
            {
                Label = nextLabel, SourcePath = path
            });

            boilerplate.Add(new OpcodePop()
            {
                Label = nextLabel, SourcePath = path
            });
            boilerplate.Add(new OpcodePush(0)
            {
                Label = nextLabel, SourcePath = path
            });
            boilerplate.Add(new OpcodeReturn(1)
            {
                Label = nextLabel, SourcePath = path
            });

            branchFromOne.DestinationLabel = branchTo.Label;
            branchFromTwo.DestinationLabel = branchTo.Label;

            return(boilerplate);
        }