internal FunctionContext ( Engine parentEngine, PFunction implementation, PValue[] args, PVariable[] sharedVariables, bool suppressInitialization) { if (parentEngine == null) throw new ArgumentNullException("parentEngine"); if (implementation == null) throw new ArgumentNullException("implementation"); if (sharedVariables == null) sharedVariables = new PVariable[] {}; if (args == null) args = new PValue[] {}; if ( !(suppressInitialization || implementation.ParentApplication._SuppressInitialization)) implementation.ParentApplication.EnsureInitialization(parentEngine); _parentEngine = parentEngine; _implementation = implementation; _bindArguments(args); _createLocalVariables(); ReturnMode = ReturnMode.Exit; if (_implementation.Meta.ContainsKey(PFunction.SharedNamesKey)) { var sharedNames = _implementation.Meta[PFunction.SharedNamesKey].List; //Ensure enough shared variables have been passed if (sharedNames.Length > sharedVariables.Length) throw new ArgumentException ( "The function " + _implementation.Id + " requires " + sharedNames.Length + " variables to be shared."); for (var i = 0; i < sharedNames.Length; i++) { if (sharedVariables[i] == null) throw new ArgumentNullException ( "sharedVariables", String.Format( "The element at index {0} passed in sharedVariables is null for function {1}.", i, implementation)); if (_localVariables.ContainsKey(sharedNames[i])) continue; //Arguments are redeclarations, that is not shared _localVariables.Add(sharedNames[i], sharedVariables[i]); } } //Populate fast variable access array (call by index) _localVariableArray = new PVariable[_localVariables.Count]; foreach (var mapping in _implementation.LocalVariableMapping) _localVariableArray[mapping.Value] = _localVariables[mapping.Key]; }
public FunctionContext ( Engine parentEngine, PFunction implementation, PValue[] args, PVariable[] sharedVariables) : this(parentEngine, implementation, args, sharedVariables, false) { }
/// <summary> /// Creates a new closure. /// </summary> /// <param name = "func">A (nested) function, that has shared variables.</param> /// <param name = "sharedVariables">A list of variables to share with the function.</param> /// <exception cref = "ArgumentNullException">Either <paramref name = "func" /> or <paramref name = "sharedVariables" /> is null.</exception> public Closure(PFunction func, PVariable[] sharedVariables) { if (func == null) throw new ArgumentNullException("func"); if (sharedVariables == null) throw new ArgumentNullException("sharedVariables"); _function = func; _sharedVariables = sharedVariables; }
/// <summary> /// Creates a new closure. /// </summary> /// <param name = "func">A (nested) function, that has shared variables.</param> /// <param name = "sharedVariables">A list of variables to share with the function.</param> /// <exception cref = "ArgumentNullException">Either <paramref name = "func" /> or <paramref name = "sharedVariables" /> is null.</exception> public CilClosure(PFunction func, PVariable[] sharedVariables) { if (func == null) throw new ArgumentNullException("func"); if (sharedVariables == null) throw new ArgumentNullException("sharedVariables"); if (!func.HasCilImplementation) throw new ArgumentException(func + " does not have a cil implemenetation"); _function = func; _sharedVariables = sharedVariables; }
private static PVariable[] _getSharedVariables(FunctionContext fctx) { var metaTable = fctx.Implementation.Meta; MetaEntry entry; if (!(metaTable.TryGetValue(PFunction.SharedNamesKey, out entry) && entry.IsList)) { return new PVariable[] {}; } var sharedNames = entry.List; var sharedVariables = new PVariable[sharedNames.Length]; for (var i = 0; i < sharedNames.Length; i++) { var name = sharedNames[i].Text; sharedVariables[i] = fctx.LocalVariables[name]; } return sharedVariables; }
public void ReturnContinueFormTryFinally() { Compile( @" function main() { try { continue; } finally { } } "); var func = target.Functions["main"]; var emptyArgV = new PValue[0]; var emptyEnvironment = new PVariable[0]; if (CompileToCil) { var nullContext = new NullContext(engine, target, new List<string>()); Assert.IsTrue(func.HasCilImplementation, "main must have CIL implementation."); ReturnMode returnMode; PValue value; func.CilImplementation( func, nullContext, emptyArgV, emptyEnvironment, out value, out returnMode); Assert.AreEqual(value.Type, PType.Null); Assert.AreEqual(returnMode, ReturnMode.Continue); } var fctx = func.CreateFunctionContext(engine, emptyArgV, emptyEnvironment); engine.Process(fctx); Assert.AreEqual(fctx.ReturnValue.Type, PType.Null); Assert.AreEqual(fctx.ReturnMode, ReturnMode.Continue); }
/// <summary> /// Implementation of <see cref = "PerformNextCycle" />. /// </summary> /// <param name = "lastContext">Stack context of a called function that just returned. Must be set if the last step called a function/pushed a new context onto the VM stack. Is ignored otherwise.</param> /// <param name = "needToReturn">Indicates whether to return after executing one instruction, even if more instructions could be combined into the cycle.</param> /// <returns>True if the context is not done yet, i.e., is to be kept on the VM stack; False if it is done, has produced a return value and should be removed from the VM stack.</returns> private bool _performNextCylce(StackContext lastContext, bool needToReturn) { //Indicates whether or not control needs to be returned to the VM. // as long as no operation is performed on the stack, // var codeBase = _implementation.Code; var codeLength = codeBase.Count; do { if (_pointer >= codeLength) { ReturnMode = ReturnMode.Exit; return false; } //Get return value if (_fetchReturnValue) { if (lastContext == null) throw new PrexoniteException("Root function tries to fetch a return value."); Push(lastContext.ReturnValue ?? PType.Null.CreatePValue()); _fetchReturnValue = false; } var ins = codeBase[_pointer++]; #if Verbose Console.Write("/* " + (_pointer-1).ToString(CultureInfo.InvariantCulture).PadLeft(4, '0') + " */ " + ins); PValue val; #endif var argc = ins.Arguments; var justEffect = ins.JustEffect; PValue[] argv; var id = ins.Id; var moduleName = ins.ModuleName; PValue left; PValue right; var t = ins.GenericArgument as PType; PVariable pvar; PFunction func; //used by static calls int idx; string methodId; MemberInfo member; #region OPCODE HANDLING Application targetApplication; switch (ins.OpCode) { #region NOP //NOP case OpCode.nop: //Do nothing break; #endregion #region LOAD #region LOAD CONSTANT //LOAD CONSTANT case OpCode.ldc_int: Push(argc); break; case OpCode.ldc_real: Push((double) ins.GenericArgument); break; case OpCode.ldc_bool: Push(argc != 0); break; case OpCode.ldc_string: Push(id); break; case OpCode.ldc_null: Push(PType.Null.CreatePValue()); break; #endregion LOAD CONSTANT #region LOAD REFERENCE //LOAD REFERENCE case OpCode.ldr_loc: if (_localVariables.ContainsKey(id)) Push(CreateNativePValue(_localVariables[id])); else throw new PrexoniteException ( string.Format ( "Cannot load reference to local variable {0} in function {1}.", id, _implementation.Id)); break; case OpCode.ldr_loci: Push(CreateNativePValue(_localVariableArray[argc])); break; case OpCode.ldr_glob: targetApplication = _getTargetApplication(moduleName); if (targetApplication.Variables.TryGetValue(id, out pvar)) Push(CreateNativePValue(pvar)); else throw new PrexoniteException ( string.Format ( "Cannot load reference to global variable {0} in application {1}.", id, targetApplication.Module.Name)); break; case OpCode.ldr_func: targetApplication = _getTargetApplication(moduleName); if (targetApplication.Functions.TryGetValue(id, out func)) Push(CreateNativePValue(func)); else throw new PrexoniteException ( string.Format ( "Cannot load reference to function {0} in application {1}.", id, targetApplication.Module.Name)); break; case OpCode.ldr_cmd: if (ParentEngine.Commands.Contains(id)) Push(CreateNativePValue(ParentEngine.Commands[id])); else throw new PrexoniteException ( string.Format ( "Cannot load reference to command {0}.", id)); break; case OpCode.ldr_app: Push(CreateNativePValue(ParentApplication)); break; case OpCode.ldr_eng: Push(CreateNativePValue(ParentEngine)); break; case OpCode.ldr_type: if ((object) t == null) { t = ConstructPType(id); ins.GenericArgument = t; } Push(CreateNativePValue(t)); break; case OpCode.ldr_mod: Debug.Assert(moduleName != null); Push(CreateNativePValue(moduleName)); break; #endregion //LOAD REFERENCE #endregion //LOAD #region VARIABLES #region LOCAL //LOAD LOCAL VARIABLE case OpCode.ldloc: pvar = _localVariables[id]; if (pvar == null) throw new PrexoniteException ( "The local variable " + id + " in function " + _implementation.Id + " does not exist."); #if Verbose val = pvar.Value; Console.Write("=" + _toDebug(val)); Push(val); #else Push(pvar.Value); #endif break; case OpCode.stloc: pvar = _localVariables[id]; if (pvar == null) throw new PrexoniteException ( "The local variable " + id + " in function " + _implementation.Id + " does not exist."); pvar.Value = Pop(); break; case OpCode.ldloci: Push(_localVariableArray[argc].Value); break; case OpCode.stloci: _localVariableArray[argc].Value = Pop(); break; #endregion #region GLOBAL //LOAD GLOBAL VARIABLE case OpCode.ldglob: targetApplication = _getTargetApplication(moduleName); if (!targetApplication.Variables.TryGetValue(id, out pvar)) throw _globalVariableDoesNotExistException(id); targetApplication.EnsureInitialization(ParentEngine); #if Verbose val = pvar.Value; Console.Write("=" + _toDebug(val)); Push(val); #else Push(pvar.Value); #endif break; case OpCode.stglob: targetApplication = _getTargetApplication(moduleName); if (!targetApplication.Variables.TryGetValue(id, out pvar)) throw _globalVariableDoesNotExistException(id); pvar.Value = Pop(); break; #endregion #endregion #region CONSTRUCTION //CONSTRUCTION case OpCode.newobj: if ((object) t == null) { t = ConstructPType(id); ins.GenericArgument = t; } _fillArgs(argc, out argv); Push(t.Construct(this, argv)); break; case OpCode.newtype: //assemble type expression _fillArgs(argc, out argv); Push(CreateNativePValue(ParentEngine.CreatePType(this, id, argv))); break; case OpCode.newclo: var vars = ins.GenericArgument as string[]; func = _getTargetApplication(moduleName).Functions[id]; if (vars == null) { MetaEntry[] entries; if (func.Meta.ContainsKey(PFunction.SharedNamesKey)) entries = func.Meta[PFunction.SharedNamesKey].List; else entries = new MetaEntry[] {}; vars = new string[entries.Length]; for (var i = 0; i < entries.Length; i++) vars[i] = entries[i].Text; ins.GenericArgument = vars; } var pvars = new PVariable[vars.Length]; for (var i = 0; i < pvars.Length; i++) pvars[i] = _localVariables[vars[i]]; if (func.HasCilImplementation) { Push(CreateNativePValue(new CilClosure(func, pvars))); } else { Push(CreateNativePValue(new Closure(func, pvars))); } break; case OpCode.newcor: _fillArgs(argc, out argv); var routine = Pop(); var routineobj = routine.Value; var routinesa = routineobj as IStackAware; if (routineobj == null) { Push(PType.Null.CreatePValue()); } else { StackContext corctx; if (routinesa != null) corctx = routinesa.CreateStackContext(this, argv); else corctx = (StackContext) routine.DynamicCall ( this, new[] { PType.Object.CreatePValue(ParentEngine), PType.Object.CreatePValue(argv) }, PCall.Get, "CreateStackContext").Value; Push ( PType.Object[typeof (Coroutine)].CreatePValue( new Coroutine(corctx))); } break; #endregion #region OPERATORS #region UNARY //UNARY OPERATORS case OpCode.incloc: pvar = _localVariables[id]; doIncrement: pvar.Value = pvar.Value.Increment(this); #if Verbose Console.Write("=" + _toDebug(pvar.Value)); #endif break; case OpCode.incloci: pvar = _localVariableArray[argc]; goto doIncrement; case OpCode.incglob: if (!ParentApplication.Variables.TryGetValue(id, out pvar)) throw _globalVariableDoesNotExistException(id); pvar.Value = pvar.Value.Increment(this); #if Verbose Console.Write("=" + _toDebug(pvar.Value)); #endif break; case OpCode.decloc: pvar = _localVariables[id]; doDecrement: pvar.Value = pvar.Value.Decrement(this); #if Verbose Console.Write("=" + _toDebug(pvar.Value)); #endif break; case OpCode.decloci: pvar = _localVariableArray[argc]; goto doDecrement; case OpCode.decglob: if (!ParentApplication.Variables.TryGetValue(id, out pvar)) throw _globalVariableDoesNotExistException(id); pvar.Value = pvar.Value.Decrement(this); #if Verbose Console.Write("=" + _toDebug(pvar.Value)); #endif break; #endregion #region BINARY //binary operators are all implemented as commands in the namespace // Prexonite.Commands.Core.Operators #endregion //OPERATORS #endregion #region TYPE OPERATIONS #region TYPE CHECK //TYPE CHECK case OpCode.check_const: if ((object) t == null) { t = ConstructPType(id); ins.GenericArgument = t; } goto check_type; //common code case OpCode.check_arg: t = (PType) Pop().Value; check_type: Push(Pop().Type.Equals(t)); break; case OpCode.check_null: Push(Pop().IsNull); break; #endregion #region TYPE CAST case OpCode.cast_const: if ((object) t == null) { t = ConstructPType(id); ins.GenericArgument = t; } goto cast_type; //common code case OpCode.cast_arg: t = (PType) Pop().Value; cast_type: Push(Pop().ConvertTo(this, t, true)); break; #endregion #endregion #region OBJECT CALLS #region DYNAMIC case OpCode.get: _fillArgs(argc, out argv); left = Pop(); right = left.DynamicCall(this, argv, PCall.Get, id); if (!justEffect) Push(right); needToReturn = true; break; case OpCode.set: _fillArgs(argc, out argv); left = Pop(); left.DynamicCall(this, argv, PCall.Set, id); needToReturn = true; break; #endregion #region STATIC case OpCode.sget: _fillArgs(argc, out argv); idx = id.LastIndexOf("::"); if (idx < 0) throw new PrexoniteException ( "Invalid sget instruction. Does not specify a method."); needToReturn = true; methodId = id.Substring(idx + 2); member = ins.GenericArgument as MemberInfo; if (member != null) goto callByMemberGet; else if ((object) t != null) goto callByTypeGet; else { //Full resolve var typeExpr = id.Substring(0, idx); t = ConstructPType(typeExpr); ins.GenericArgument = t; if (t is ObjectPType) { //Try to get a member info var objT = (ObjectPType) t; right = objT.StaticCall(this, argv, PCall.Get, methodId, out member); if (!justEffect) Push(right); if (member != null) ins.GenericArgument = member; break; } else goto callByTypeGet; } callByTypeGet: right = t.StaticCall(this, argv, PCall.Get, methodId); if (!justEffect) Push(right); break; callByMemberGet: right = ObjectPType._execute(this, member, argv, PCall.Get, methodId, null); if (!justEffect) Push(right); break; case OpCode.sset: _fillArgs(argc, out argv); idx = id.LastIndexOf("::"); if (idx < 0) throw new PrexoniteException ( "Invalid sget instruction. Does not specify a method."); needToReturn = true; methodId = id.Substring(idx + 2); member = ins.GenericArgument as MemberInfo; if (member != null) goto callByMemberSet; else if ((object) t != null) goto callByTypeSet; else { //Full resolve var typeExpr = id.Substring(0, idx); t = ConstructPType(typeExpr); ins.GenericArgument = t; if (t is ObjectPType) { //Try to get a member info var objT = (ObjectPType) t; objT.StaticCall(this, argv, PCall.Set, methodId, out member); if (member != null) ins.GenericArgument = member; break; } else goto callByTypeSet; } callByTypeSet: t.StaticCall(this, argv, PCall.Set, methodId); break; callByMemberSet: ObjectPType._execute(this, member, argv, PCall.Set, methodId, null); break; #endregion #endregion #region INDIRECT CALLS case OpCode.indloc: _fillArgs(argc, out argv); pvar = _localVariables[id]; if (pvar == null) throw new PrexoniteException("The local variable " + id + " resolved to null in function " + Implementation.Id); left = _localVariables[id].Value; #if Verbose Console.Write(" " + _toDebug(left) + "("); foreach (PValue arg in argv) Console.Write(_toDebug(arg) + ", "); Console.WriteLine(")"); #endif //Perform indirect call doIndloc: { var stackAware = left.Value as IStackAware; if (stackAware != null && left.Type is ObjectPType) { _fetchReturnValue = !justEffect; ParentEngine.Stack.AddLast(stackAware.CreateStackContext(this, argv)); } else { if (justEffect) left.IndirectCall(this, argv); else Push(left.IndirectCall(this, argv)); } } needToReturn = true; break; case OpCode.indloci: idx = argc & ushort.MaxValue; argc = (argc & (ushort.MaxValue << 16)) >> 16; _fillArgs(argc, out argv); left = _localVariableArray[idx].Value; goto doIndloc; case OpCode.indglob: _fillArgs(argc, out argv); targetApplication = _getTargetApplication(moduleName); if (!targetApplication.Variables.TryGetValue(id, out pvar)) throw _globalVariableDoesNotExistException(id); targetApplication.EnsureInitialization(ParentEngine); left = pvar.Value; #if Verbose Console.Write(" " + _toDebug(left) + "("); foreach (PValue arg in argv) Console.Write(_toDebug(arg) + ", "); Console.WriteLine(")"); #endif //Perform indirect call goto doIndloc; case OpCode.indarg: _fillArgs(argc, out argv); left = Pop(); goto doIndloc; case OpCode.tail: _fillArgs(argc, out argv); left = Pop(); var stack = _parentEngine.Stack; // ReSharper disable AssignNullToNotNullAttribute stack.Remove(stack.FindLast(this)); // ReSharper restore AssignNullToNotNullAttribute stack.AddLast(Call.CreateStackContext(this, left, argv)); return false; #endregion #region ENGINE CALLS case OpCode.func: _fillArgs(argc, out argv); if (ParentEngine.CacheFunctions) { func = (ins.GenericArgument as PFunction) ?? _getTargetApplication(moduleName).Functions[id]; ins.GenericArgument = func; } else { func = _getTargetApplication(moduleName).Functions[id]; } if (func == null) throw PrexoniteRuntimeException.CreateRuntimeException ( this, "No function with the physical name " + id + " exists."); #if NoCil FunctionContext fctx = new FunctionContext( ParentEngine, func, argv); _fetchReturnValue = !justEffect; ParentEngine.Stack.AddLast(fctx); #else if (func.HasCilImplementation) { ReturnMode returnMode; func.CilImplementation(func, this, argv, null, out left, out returnMode); ReturnMode = returnMode; if (!justEffect) Push(left); } else { var fctx = new FunctionContext ( ParentEngine, func, argv); _fetchReturnValue = !justEffect; ParentEngine.Stack.AddLast(fctx); } #endif #if Verbose Console.Write("\n#PSH: {0}/{1},{2}(", id, ParentApplication.Module.Name.Id, ParentApplication.Module.Name.Version); foreach (PValue arg in argv) Console.Write(_toDebug(arg) + ", "); Console.WriteLine(")"); #endif return true; //Force the engine to keep this context on the stack for another cycle case OpCode.cmd: _fillArgs(argc, out argv); needToReturn = true; PCommand cmd; if (ParentEngine.CacheCommands) { cmd = (ins.GenericArgument as PCommand) ?? ParentEngine.Commands[id]; ins.GenericArgument = cmd; } else { cmd = ParentEngine.Commands[id]; } if (cmd == null) throw new PrexoniteException("Cannot find command " + id + "!"); var sa = cmd as IStackAware; if (sa != null) { var cctx = sa.CreateStackContext(this, argv); _fetchReturnValue = !justEffect; ParentEngine.Stack.AddLast(cctx); } else { if (justEffect) cmd.Run(this, argv); else { #if Verbose val = cmd.Run(this, argv); Console.Write(" =" + _toDebug(val)); Push(val); #else Push(cmd.Run(this, argv)); #endif } } break; #endregion #region FLOW CONTROL //FLOW CONTROL #region JUMPS case OpCode.jump: _pointer = argc; break; case OpCode.jump_t: left = Pop(); if (!(left.Value is bool)) left = left.ConvertTo(this, PType.Bool); if ((bool) left.Value) { _pointer = argc; #if Verbose Console.Write(" -> jump"); #endif } break; case OpCode.jump_f: left = Pop(); if (!(left.Value is bool)) left = left.ConvertTo(this, PType.Bool); if (!(bool) left.Value) { #if Verbose Console.Write(" -> jump"); #endif _pointer = argc; } break; #endregion #region RETURNS case OpCode.ret_exit: ReturnMode = ReturnMode.Exit; #if Verbose Console.WriteLine(); #endif return false; case OpCode.ret_value: _returnValue = Pop(); ReturnMode = ReturnMode.Exit; #if Verbose Console.WriteLine("=" + _toDebug(_returnValue)); #endif return false; case OpCode.ret_break: ReturnMode = ReturnMode.Break; #if Verbose Console.WriteLine(); #endif return false; case OpCode.ret_continue: ReturnMode = ReturnMode.Continue; #if Verbose Console.WriteLine(); #endif return false; case OpCode.ret_set: _returnValue = Pop(); #if Verbose Console.WriteLine("=" + _toDebug(_returnValue)); #endif break; #endregion #region THROW case OpCode.@throw: left = Pop(); t = left.Type; PrexoniteRuntimeException prexc; if (t is StringPType) prexc = PrexoniteRuntimeException.CreateRuntimeException ( this, (string) left.Value); else if (t is ObjectPType && left.Value is Exception) prexc = PrexoniteRuntimeException.CreateRuntimeException ( this, ((Exception) left.Value).Message, (Exception) left.Value); else prexc = PrexoniteRuntimeException.CreateRuntimeException ( this, left.CallToString(this)); #if Verbose Console.WriteLine(); #endif throw prexc; #endregion #region LEAVE case OpCode.@try: _isHandlingException.Push(false); break; case OpCode.leave: if (_isHandlingException.Count == 0) { throw new PrexoniteException( "Unexpected leave instruction. This happens when jumping to an instruction in a try block from the outside."); } else if (!_isHandlingException.Pop()) { //No exception to handle _pointer = argc; #if Verbose Console.Write(" => Skip catch block."); #endif } else { if (_currentTry.HasCatch) { //Exception handled by user code #if Verbose Console.Write(" => execute catch({0}:{1})", _currentException.GetType().Name, _currentException.Message); #endif } else { //Rethrow exception throw _currentException; } } break; #endregion #region EXCEPTION case OpCode.exc: Push(CreateNativePValue(_currentException)); break; #endregion #endregion #region STACK MANIPULATION //STACK MANIPULATION case OpCode.pop: if (_stack.Count < argc) _throwInvalidStackException(argc); for (var i = 0; i < argc; i++) Pop(); //Pop to nirvana break; case OpCode.dup: left = Peek(); for (var i = 0; i < argc; i++) Push(left); break; case OpCode.rot: var values = (int) ins.GenericArgument; var rotations = argc; var target = new PValue[values]; for (var i = 0; i < values; i++) target[(i + rotations)%values] = Pop(); for (var i = values - 1; i >= 0; i--) Push(target[i]); break; #endregion } #endregion //Next instruction #if Verbose Console.Write("\n"); #endif if (_pointer >= codeLength) return false; } while (!needToReturn); return _pointer < codeLength; }
public void ReplaceLocalVariable(string name, PVariable newVariable) { if (String.IsNullOrEmpty(name)) throw new ArgumentNullException("name"); if (newVariable == null) throw new ArgumentNullException("newVariable"); if (_implementation.LocalVariableMapping.ContainsKey(name)) _localVariableArray[_implementation.LocalVariableMapping[name]] = newVariable; _localVariables[name] = newVariable; }
private void _bindArguments(PValue[] args) { //Create args variable const string argVId = PFunction.ArgumentListId; if (_implementation.Variables.Contains(argVId)) { var argsV = new PVariable { Value = _parentEngine.CreateNativePValue(args) }; _localVariables.Add(argVId, argsV); } //Create actual parameter variables var i = 0; foreach (var arg in _implementation.Parameters) { var pvar = new PVariable(); if (i < args.Length) pvar.Value = args[i++]; //Ensure it is a PValue if (pvar.Value == null) pvar.Value = PType.Null.CreatePValue(); _localVariables.Add(arg, pvar); } }