/////////////////////////////////////////////////////////////////////// #region IExecute Members public override ReturnCode Execute( Interpreter interpreter, IClientData clientData, ArgumentList arguments, ref Result result ) { ReturnCode code = ReturnCode.Ok; if (interpreter != null) { if (arguments != null) { IScriptLocation location = null; code = ScriptOps.GetAndCheckProcedureLocation( interpreter, this, ref location, ref result); if (code == ReturnCode.Ok) { string procedureName = this.Name; ArgumentList procedureArguments = this.Arguments; if (procedureArguments != null) { bool hasArgs = procedureArguments.IsVariadic(true); int totalArgs = hasArgs ? procedureArguments.Count - 1 : procedureArguments.Count; int optionalArgs = procedureArguments.GetOptionalCount(); if ((arguments.Count > 0) && ((((arguments.Count - 1) >= (totalArgs - optionalArgs)) && ((arguments.Count - 1) <= totalArgs)) || (hasArgs && ((arguments.Count - 1) >= (totalArgs - optionalArgs))))) { ICallFrame frame = null; try { frame = interpreter.NewProcedureCallFrame( procedureName, CallFrameFlags.Procedure, null, this, arguments); VariableDictionary variables = frame.Variables; frame.ProcedureArguments = new ArgumentList(arguments[0]); for (int argumentIndex = 0; argumentIndex < procedureArguments.Count; argumentIndex++) { string varName = procedureArguments[argumentIndex].Name; if (!variables.ContainsKey(varName)) { ArgumentFlags flags = ArgumentFlags.None; object varValue; if (hasArgs && (argumentIndex == (procedureArguments.Count - 1))) { // // NOTE: This argument is part of an argument list. // flags |= ArgumentFlags.ArgumentList; // // NOTE: Build the list for the final formal argument value, // which consists of all the remaining argument values. // ArgumentList argsArguments = new ArgumentList(); for (int argsArgumentIndex = argumentIndex + 1; argsArgumentIndex < arguments.Count; argsArgumentIndex++) { // // NOTE: Sync up the argument name and flags for use when // debugging (below). // Argument argsArgument = Argument.GetOrCreate( interpreter, arguments[argsArgumentIndex].Flags | flags, String.Format("{0}{1}{2}", varName, Characters.Space, argsArguments.Count), arguments[argsArgumentIndex], interpreter.HasNoCacheArgument()); argsArguments.Add(argsArgument); } varValue = argsArguments; } else { if ((argumentIndex + 1) < arguments.Count) { // // NOTE: Sync up the argument name for use when // debugging (below) and use the value // supplied by the caller. // varValue = Argument.GetOrCreate(interpreter, arguments[argumentIndex + 1].Flags | flags, varName, arguments[argumentIndex + 1], interpreter.HasNoCacheArgument()); } else { // // NOTE: We cannot sync up the argument name here // because we are out-of-bounds on that list // and it cannot be extended (i.e. it would // break [info level]); therefore, we punt // on that for now. Use the default value // for this argument, if any; otherwise, use // an empty string. // object @default = procedureArguments[argumentIndex].Default; varValue = (@default != null) ? @default : Argument.NoValue; } } code = interpreter.SetVariableValue2(VariableFlags.Argument, frame, varName, varValue, ref result); if (code != ReturnCode.Ok) { break; } // // BUGFIX: Now, also keep track of this argument in the procedure // arguments list. Primarily because we do not want to // have to redo this logic later (i.e. for [scope]). // frame.ProcedureArguments.Add(Argument.GetOrCreate( interpreter, flags, varName, varValue, interpreter.HasNoCacheArgument())); } } // // NOTE: Make sure we succeeded in creating the call frame. // if (code == ReturnCode.Ok) { ICallFrame savedFrame = null; interpreter.PushProcedureCallFrame(frame, true, ref savedFrame); try { #if DEBUGGER && DEBUGGER_EXECUTE if (DebuggerOps.CanHitBreakpoints(interpreter, EngineFlags.None, BreakpointType.BeforeProcedureBody)) { code = interpreter.CheckBreakpoints( code, BreakpointType.BeforeProcedureBody, procedureName, null, null, this, null, clientData, arguments, ref result); } #endif if (code == ReturnCode.Ok) { bool locked = false; try { bool atomic = EntityOps.IsAtomic(this); if (atomic) { interpreter.InternalTryLock(ref locked); /* TRANSACTIONAL */ } if (!atomic || locked) { #if ARGUMENT_CACHE || PARSE_CACHE EngineFlags savedEngineFlags = EngineFlags.None; bool nonCaching = EntityOps.IsNonCaching(this); if (nonCaching) { interpreter.BeginProcedureBodyNoCaching( ref savedEngineFlags); } try { #endif string body = this.Body; interpreter.ReturnCode = ReturnCode.Ok; code = interpreter.EvaluateScript( body, location, ref result); #if ARGUMENT_CACHE || PARSE_CACHE } finally { if (nonCaching) { interpreter.EndProcedureBodyNoCaching( ref savedEngineFlags); } } #endif } else { result = "could not lock interpreter"; code = ReturnCode.Error; } } finally { interpreter.InternalExitLock(ref locked); /* TRANSACTIONAL */ } #if DEBUGGER && DEBUGGER_EXECUTE if (DebuggerOps.CanHitBreakpoints(interpreter, EngineFlags.None, BreakpointType.AfterProcedureBody)) { code = interpreter.CheckBreakpoints( code, BreakpointType.AfterProcedureBody, procedureName, null, null, this, null, clientData, arguments, ref result); } #endif // // BUGFIX: If an opaque object handle is being returned, add // a reference to it now. // if ((code == ReturnCode.Ok) || (code == ReturnCode.Return)) { code = interpreter.AddObjectReference( code, result, ObjectReferenceType.Return, ref result); } if (code == ReturnCode.Return) { code = Engine.UpdateReturnInformation(interpreter); } else if (code == ReturnCode.Error) { Engine.AddErrorInformation(interpreter, result, String.Format("{0} (procedure \"{1}\" line {2})", Environment.NewLine, FormatOps.Ellipsis(procedureName), Interpreter.GetErrorLine(interpreter))); } } } finally { /* IGNORED */ interpreter.PopProcedureCallFrame(frame, ref savedFrame); } } } finally { if (frame != null) { IDisposable disposable = frame as IDisposable; if (disposable != null) { disposable.Dispose(); disposable = null; } frame = null; } } } else { if (procedureArguments.Count > 0) { result = String.Format( "wrong # args: should be \"{0} {1}\"", Parser.Quote(procedureName), procedureArguments.ToRawString(ToStringFlags.Decorated, Characters.Space.ToString())); } else { result = String.Format( "wrong # args: should be \"{0}\"", Parser.Quote(procedureName)); } code = ReturnCode.Error; } } else { result = "invalid procedure argument list"; code = ReturnCode.Error; } } } else { result = "invalid argument list"; code = ReturnCode.Error; } } else { result = "invalid interpreter"; code = ReturnCode.Error; } return(code); }
/////////////////////////////////////////////////////////////////////////////////////////////// #region IExecute Members public override ReturnCode Execute( Interpreter interpreter, IClientData clientData, ArgumentList arguments, ref Result result ) { ReturnCode code = ReturnCode.Ok; if (interpreter != null) { if (arguments != null) { if (arguments.Count >= 2) { // // NOTE: lambdaExpr must be a two element list {args body} or a three element // list {args body namespace}. // StringList lambdaExpr = null; code = Parser.SplitList( interpreter, arguments[1], 0, Length.Invalid, true, ref lambdaExpr, ref result); if (code == ReturnCode.Ok) { if ((lambdaExpr.Count == 2) || (lambdaExpr.Count == 3)) { byte[] hashValue = arguments[1].GetHashValue(ref result); if (hashValue != null) { INamespace @namespace = null; if (lambdaExpr.Count == 3) { @namespace = NamespaceOps.Lookup( interpreter, lambdaExpr[2], true, false, ref result); if (@namespace == null) { code = ReturnCode.Error; } } if (code == ReturnCode.Ok) { // // NOTE: Parse the arguments into a list and make sure there are enough // supplied to satisfy the request. // StringList list = null; code = Parser.SplitList( interpreter, lambdaExpr[0], 0, Length.Invalid, true, ref list, ref result); if (code == ReturnCode.Ok) { StringPairList list2 = new StringPairList(); for (int argumentIndex = 0; argumentIndex < list.Count; argumentIndex++) { StringList list3 = null; code = Parser.SplitList( interpreter, list[argumentIndex], 0, Length.Invalid, true, ref list3, ref result); if (code != ReturnCode.Ok) { break; } if (list3.Count > 2) { result = String.Format( "too many fields in argument specifier \"{0}\"", list[argumentIndex]); code = ReturnCode.Error; break; } else if ((list3.Count == 0) || String.IsNullOrEmpty(list3[0])) { result = "argument with no name"; code = ReturnCode.Error; break; } else if (!Parser.IsSimpleScalarVariableName(list3[0], String.Format(Interpreter.ArgumentNotSimpleError, list3[0]), String.Format(Interpreter.ArgumentNotScalarError, list3[0]), ref result)) { code = ReturnCode.Error; break; } string argName = list3[0]; string argDefault = (list3.Count >= 2) ? list3[1] : null; list2.Add(new StringPair(argName, argDefault)); } if (code == ReturnCode.Ok) { // // NOTE: We *MUST* have the formal arguments in an actual ArgumentList // container now. The variadic and optional argument semantics // depend on it. // ArgumentList formalArguments = new ArgumentList( list2, ArgumentFlags.NameOnly); // // NOTE: Compare lambda argument count with the total outer argument // count minus the "apply" and "lambdaExpr" arguments. // bool hasArgs = formalArguments.IsVariadic(true); int totalArgs = hasArgs ? formalArguments.Count - 1 : formalArguments.Count; int optionalArgs = formalArguments.GetOptionalCount(); if ((((arguments.Count - 2) >= (totalArgs - optionalArgs)) && ((arguments.Count - 2) <= totalArgs)) || (hasArgs && ((arguments.Count - 2) >= (totalArgs - optionalArgs)))) { string name = NextName(interpreter, @namespace); ICallFrame frame = null; try { frame = interpreter.NewProcedureCallFrame( name, CallFrameFlags.Procedure | CallFrameFlags.Lambda, new ClientData(hashValue), this, arguments); VariableDictionary variables = frame.Variables; frame.ProcedureArguments = new ArgumentList(arguments[0]); for (int argumentIndex = 0; argumentIndex < formalArguments.Count; argumentIndex++) { string varName = formalArguments[argumentIndex].Name; if (!variables.ContainsKey(varName)) { ArgumentFlags flags = ArgumentFlags.None; object varValue; if (hasArgs && (argumentIndex == (formalArguments.Count - 1))) { // // NOTE: This argument is part of an argument list. // flags |= ArgumentFlags.ArgumentList; // // NOTE: Build the list for the final formal argument value, // which consists of all the remaining argument values. // ArgumentList argsArguments = new ArgumentList(); for (int argsArgumentIndex = argumentIndex + 2; argsArgumentIndex < arguments.Count; argsArgumentIndex++) { // // NOTE: Sync up the argument name and flags for use when // debugging (below). // Argument argsArgument = Argument.GetOrCreate( interpreter, arguments[argsArgumentIndex].Flags | flags, String.Format("{0}{1}{2}", varName, Characters.Space, argsArguments.Count), arguments[argsArgumentIndex], interpreter.HasNoCacheArgument()); argsArguments.Add(argsArgument); } varValue = argsArguments; } else { if ((argumentIndex + 2) < arguments.Count) { // // NOTE: Sync up the argument name for use when // debugging (below) and use the value // supplied by the caller. // varValue = Argument.GetOrCreate(interpreter, arguments[argumentIndex + 2].Flags | flags, varName, arguments[argumentIndex + 2], interpreter.HasNoCacheArgument()); } else { // // NOTE: We cannot sync up the argument name here // because we are out-of-bounds on that list // and it cannot be extended (i.e. it would // break [info level]); therefore, we punt // on that for now. Use the default value // for this argument, if any; otherwise, use // an empty string. // object @default = formalArguments[argumentIndex].Default; varValue = (@default != null) ? @default : Argument.NoValue; } } code = interpreter.SetVariableValue2(VariableFlags.Argument, frame, varName, varValue, ref result); if (code != ReturnCode.Ok) { break; } // // BUGFIX: Now, also keep track of this argument in the procedure // arguments list. Primarily because we do not want to // have to redo this logic later (i.e. for [scope]). // frame.ProcedureArguments.Add(Argument.GetOrCreate( interpreter, flags, varName, varValue, interpreter.HasNoCacheArgument())); } } // // NOTE: Make sure we succeeded in creating the call frame. // if (code == ReturnCode.Ok) { ICallFrame savedFrame = null; interpreter.PushProcedureCallFrame(frame, true, ref savedFrame); try { #if DEBUGGER && DEBUGGER_EXECUTE if (DebuggerOps.CanHitBreakpoints(interpreter, EngineFlags.None, BreakpointType.BeforeLambdaBody)) { code = interpreter.CheckBreakpoints( code, BreakpointType.BeforeLambdaBody, this.Name, null, null, this, null, clientData, arguments, ref result); } #endif if (code == ReturnCode.Ok) { interpreter.ReturnCode = ReturnCode.Ok; code = interpreter.EvaluateScript(lambdaExpr[1], arguments[1], ref result); #if DEBUGGER && DEBUGGER_EXECUTE if (DebuggerOps.CanHitBreakpoints(interpreter, EngineFlags.None, BreakpointType.AfterLambdaBody)) { code = interpreter.CheckBreakpoints( code, BreakpointType.AfterLambdaBody, this.Name, null, null, this, null, clientData, arguments, ref result); } #endif // // BUGFIX: If an opaque object handle is being returned, add // a reference to it now. // if ((code == ReturnCode.Ok) || (code == ReturnCode.Return)) { code = interpreter.AddObjectReference( code, result, ObjectReferenceType.Return, ref result); } if (code == ReturnCode.Return) { code = Engine.UpdateReturnInformation(interpreter); } else if (code == ReturnCode.Error) { Engine.AddErrorInformation(interpreter, result, String.Format("{0} (lambda term \"{1}\" line {2})", Environment.NewLine, FormatOps.Ellipsis(arguments[1]), Interpreter.GetErrorLine(interpreter))); } } } finally { /* IGNORED */ interpreter.PopProcedureCallFrame(frame, ref savedFrame); } } } finally { if (frame != null) { IDisposable disposable = frame as IDisposable; if (disposable != null) { disposable.Dispose(); disposable = null; } frame = null; } } } else { result = String.Format( "wrong # args: should be \"apply lambdaExpr {0}\"", formalArguments.ToRawString(ToStringFlags.Decorated, Characters.Space.ToString())); code = ReturnCode.Error; } } } } } else { code = ReturnCode.Error; } } else { result = String.Format( "can't interpret \"{0}\" as a lambda expression", arguments[1]); code = ReturnCode.Error; } } } else { result = "wrong # args: should be \"apply lambdaExpr ?arg1 arg2 ...?\""; code = ReturnCode.Error; } } else { result = "invalid argument list"; code = ReturnCode.Error; } } else { result = "invalid interpreter"; code = ReturnCode.Error; } return(code); }
public override ReturnCode Execute( Interpreter interpreter, IClientData clientData, ArgumentList arguments, ref Result result ) { ReturnCode code = ReturnCode.Ok; if (interpreter != null) { if (arguments != null) { if ((arguments.Count == 5) || (arguments.Count == 6)) { /////////////////////////////////////////////////////////////////////////////////////////////// // // test name description ?constraints? body result // /////////////////////////////////////////////////////////////////////////////////////////////// string name = arguments[1]; #if DEBUGGER if (DebuggerOps.CanHitBreakpoints(interpreter, EngineFlags.None, BreakpointType.Test)) { code = interpreter.CheckBreakpoints( code, BreakpointType.Test, name, null, null, this, null, clientData, arguments, ref result); } if (code == ReturnCode.Ok) #endif { string description = arguments[2]; string constraints; string body; IScriptLocation bodyLocation; string expectedResult; if (arguments.Count == 6) { constraints = arguments[3]; body = arguments[4]; bodyLocation = arguments[4]; expectedResult = arguments[5]; } else { constraints = null; body = arguments[3]; bodyLocation = arguments[3]; expectedResult = arguments[4]; } ReturnCodeList returnCodes = new ReturnCodeList(new ReturnCode[] { ReturnCode.Ok, ReturnCode.Return }); MatchMode mode = StringOps.DefaultResultMatchMode; bool noCase = false; /////////////////////////////////////////////////////////////////////////////////////////////// int testLevels = interpreter.EnterTestLevel(); try { // // NOTE: Create a place to put all the output of the this command. // StringBuilder testData = StringOps.NewStringBuilder(); // // NOTE: Are we going to skip this test? // bool skip = false; bool fail = true; code = TestOps.CheckConstraints( interpreter, testLevels, name, constraints, false, false, testData, ref skip, ref fail, ref result); // // NOTE: Track the fact that we handled this test. // int[] testStatistics = null; if (code == ReturnCode.Ok) { testStatistics = interpreter.TestStatistics; if ((testStatistics != null) && (testLevels == 1) && skip) { Interlocked.Increment(ref testStatistics[ (int)TestInformationType.Total]); } } if ((code == ReturnCode.Ok) && !skip) { code = TestOps.RecordInformation( interpreter, TestInformationType.Counts, name, null, true, ref result); } // // NOTE: Check test constraints to see if we should run the test. // if ((code == ReturnCode.Ok) && !skip) { ReturnCode bodyCode = ReturnCode.Ok; Result bodyResult = null; // // NOTE: Only run the test body if the setup is successful. // if (body != null) { TestOps.AppendFormat( interpreter, testData, TestOutputType.Start, "---- {0} start", name); TestOps.AppendLine( interpreter, testData, TestOutputType.Start); int savedPreviousLevels = interpreter.BeginNestedExecution(); try { ICallFrame frame = interpreter.NewTrackingCallFrame( StringList.MakeList(this.Name, "body", name), CallFrameFlags.Test); interpreter.PushAutomaticCallFrame(frame); try { bodyCode = interpreter.EvaluateScript( body, bodyLocation, ref bodyResult); if ((bodyResult == null) && ScriptOps.HasFlags( interpreter, InterpreterFlags.TestNullIsEmpty, true)) { bodyResult = String.Empty; } if (bodyCode == ReturnCode.Error) { /* IGNORED */ interpreter.CopyErrorInformation( VariableFlags.None, ref bodyResult); } } finally { // // NOTE: Pop the original call frame that we pushed above // and any intervening scope call frames that may be // leftover (i.e. they were not explicitly closed). // /* IGNORED */ interpreter.PopScopeCallFramesAndOneMore(); } } catch (Exception e) { bodyResult = e; bodyCode = ReturnCode.Error; } finally { interpreter.EndNestedExecution(savedPreviousLevels); } } // // NOTE: Did we fail to match the return code? // bool codeFailure = !returnCodes.Contains(bodyCode); // // NOTE: Does the actual result match the expected result? // bool scriptFailure = false; ReturnCode scriptCode = ReturnCode.Ok; Result scriptResult = null; if (!codeFailure) { if (expectedResult != null) { scriptCode = TestOps.Match( interpreter, mode, bodyResult, expectedResult, noCase, null, TestOps.RegExOptions, false, ref scriptFailure, ref scriptResult); if (scriptCode == ReturnCode.Ok) { scriptFailure = !scriptFailure; } else { scriptFailure = true; } } } // // NOTE: If any of the important things failed, the test fails. // if (!(codeFailure || scriptFailure)) { // // PASS: Test ran with no errors and the results match // what we expected. // if ((testStatistics != null) && (testLevels == 1)) { Interlocked.Increment(ref testStatistics[ (int)TestInformationType.Passed]); Interlocked.Increment(ref testStatistics[ (int)TestInformationType.Total]); } TestOps.AppendFormat( interpreter, testData, TestOutputType.Pass, "++++ {0} PASSED", name); TestOps.AppendLine( interpreter, testData, TestOutputType.Pass); } else { // // FAIL: Test ran with errors or the result does not match // what we expected. // if ((testStatistics != null) && (testLevels == 1)) { if (fail) { Interlocked.Increment(ref testStatistics[ (int)TestInformationType.Failed]); Interlocked.Increment(ref testStatistics[ (int)TestInformationType.Total]); } } // // NOTE: Keep track of each test that fails. // if (testLevels == 1) { TestOps.RecordInformation( interpreter, TestInformationType.FailedNames, name, null, true); } TestOps.AppendLine( interpreter, testData, TestOutputType.Fail); TestOps.AppendFormat( interpreter, testData, TestOutputType.Fail, "==== {0} {1} {2}", name, description.Trim(), fail ? "FAILED" : "IGNORED"); TestOps.AppendLine( interpreter, testData, TestOutputType.Fail); if (body != null) { TestOps.AppendLine( interpreter, testData, TestOutputType.Body, "==== Contents of test case:"); TestOps.AppendLine( interpreter, testData, TestOutputType.Body, body); } if (scriptFailure) { if (scriptCode == ReturnCode.Ok) { TestOps.AppendLine( interpreter, testData, TestOutputType.Reason, "---- Result was:"); TestOps.AppendLine( interpreter, testData, TestOutputType.Reason, bodyResult); TestOps.AppendFormat( interpreter, testData, TestOutputType.Reason, "---- Result should have been ({0} matching):", mode); TestOps.AppendLine( interpreter, testData, TestOutputType.Reason); TestOps.AppendLine( interpreter, testData, TestOutputType.Reason, expectedResult); } else { TestOps.Append( interpreter, testData, TestOutputType.Reason, "---- Error testing result: "); TestOps.AppendLine( interpreter, testData, TestOutputType.Reason, scriptResult); if ((scriptResult != null) && (scriptResult.ErrorInfo != null)) { TestOps.Append( interpreter, testData, TestOutputType.Error, "---- errorInfo(matchResult): "); TestOps.AppendLine( interpreter, testData, TestOutputType.Error, scriptResult.ErrorInfo); TestOps.Append( interpreter, testData, TestOutputType.Error, "---- errorCode(matchResult): "); TestOps.AppendLine( interpreter, testData, TestOutputType.Error, scriptResult.ErrorCode); } } } if (codeFailure) { ReturnCodeDictionary returnCodeMessages = interpreter.TestReturnCodeMessages; string codeMessage; if ((returnCodeMessages == null) || (!returnCodeMessages.TryGetValue( bodyCode, out codeMessage) && !returnCodeMessages.TryGetValue( ReturnCode.Invalid, out codeMessage))) { codeMessage = "Unknown"; } TestOps.AppendFormat( interpreter, testData, TestOutputType.Reason, "---- {0}; Return code was: {1}", codeMessage, bodyCode); TestOps.AppendLine( interpreter, testData, TestOutputType.Reason); TestOps.Append( interpreter, testData, TestOutputType.Reason, "---- Return code should have been one of: "); TestOps.AppendLine( interpreter, testData, TestOutputType.Reason, returnCodes.ToString()); if ((bodyResult != null) && (bodyResult.ErrorInfo != null) && !returnCodes.Contains(ReturnCode.Error)) { TestOps.Append( interpreter, testData, TestOutputType.Error, "---- errorInfo(body): "); TestOps.AppendLine( interpreter, testData, TestOutputType.Error, bodyResult.ErrorInfo); TestOps.Append( interpreter, testData, TestOutputType.Error, "---- errorCode(body): "); TestOps.AppendLine( interpreter, testData, TestOutputType.Error, bodyResult.ErrorCode); } } TestOps.AppendFormat( interpreter, testData, TestOutputType.Fail, "==== {0} {1}", name, fail ? "FAILED" : "IGNORED"); TestOps.AppendLine( interpreter, testData, TestOutputType.Fail); } } // // NOTE: Did the above code succeed? // if (code == ReturnCode.Ok) { // // NOTE: The result is the complete output produced by the // entire test. // if (testData != null) { result = testData; } else { result = String.Empty; } } } catch (Exception e) { Engine.SetExceptionErrorCode(interpreter, e); result = e; code = ReturnCode.Error; } finally { interpreter.ExitTestLevel(); } } } else { result = String.Format( "wrong # args: should be \"{0} name description constraints body result\"", this.Name); code = ReturnCode.Error; } } else { result = "invalid argument list"; code = ReturnCode.Error; } } else { result = "invalid interpreter"; code = ReturnCode.Error; } return(code); }