/// <summary> /// DScript exposes a value cache. The backing store is kept inside of 'IEvaluationScheduler'. /// Values from this cache should never be returned directly; instead, the result should be cloned first /// (to avoid exposing an observable side effect). /// </summary> private EvaluationResult GetOrAdd(Context context, ModuleLiteral env, EvaluationStackFrame args) { Args.CheckArgumentIndex(args, 0); var key = args[0]; var closure = Args.AsClosure(args, 1); return(DoGetOrAdd(context, env, key, null, closure)); }
private EvaluationResult GetOrAdd(Context context, ModuleLiteral env, EvaluationStackFrame args) { Args.CheckArgumentIndex(args, 0); var key = args[0]; var closure = Args.AsClosure(args, 1); var helper = new HashingHelper(context.PathTable, recordFingerprintString: false); // Add the qualifier to the key var qualifierId = context.LastActiveModuleQualifier.QualifierId; var qualifierDisplayString = context.ContextTree.FrontEndContext.QualifierTable.GetQualifier(qualifierId).ToDisplayString(StringTable); helper.Add(qualifierDisplayString); if (!TryHashValue(key, helper)) { return(EvaluationResult.Error); } var keyFingerprint = helper.GenerateHash(); var resultToClone = context.ContextTree.ValueCache.GetOrAdd( keyFingerprint, _ => { int paramsCount = closure.Function.Params; var newValue = context.InvokeClosure(closure, closure.Frame); if (newValue.IsErrorValue) { return(EvaluationResult.Error); } return(newValue); } ); // The object returned will always be a cloned copy. // DScript is a side effect free language, but we use object identity // for equality comparison so to avoid making cache hits observable to // users we opt to clone the value from the cache each time, even after the // first time we add it to the cache. // This is also the reason why we don't have separate functions for inspecting or simply adding // because the results would be observable and potentially invalidating all the // incremental evaluations in DScript. return(DeepCloneValue(resultToClone)); }
private EvaluationResult ExpectFailure(Context context, ModuleLiteral env, EvaluationStackFrame args) { var closure = Args.AsClosure(args, 0); var expectedResults = Args.AsArrayLiteral(args, 1); using (var frame = EvaluationStackFrame.Create(closure.Function, args.Frame)) { var result = context.InvokeClosure(closure, frame); if (!result.IsErrorValue) { Assert.True(false, "Expected the code under test to throw a failure, but none was returned."); } if (!m_getDiagnostics().Any(diagnostic => diagnostic.Level == EventLevel.Error)) { Assert.True(false, "Expected to see at least one reported error, but none encountered."); } for (int i = 0; i < expectedResults.Count; i++) { if (expectedResults[i].Value is string expectedContent) { // String case Assert.False(string.IsNullOrEmpty(expectedContent), "Empty strings are not supported as expected error messages"); ValidateExpectedMessageLogged(null, expectedContent); } else { // Object case var obj = Converter.ExpectObjectLiteral( expectedResults[i], new ConversionContext(pos: i, objectCtx: expectedResults)); var code = Converter.ExtractInt(obj, m_testingExpectedMessageCode); expectedContent = Converter.ExtractString(obj, m_testingExpectedMessageContent); ValidateExpectedMessageLogged(code, expectedContent); } } return(EvaluationResult.Undefined); } }
private EvaluationResult AddIfLazy(Context context, ModuleLiteral env, EvaluationStackFrame args) { var condition = Args.AsBool(args, 0); if (!condition) { // Return empty Array var entry = context.TopStack; return(EvaluationResult.Create(ArrayLiteral.CreateWithoutCopy(new EvaluationResult[0], entry.InvocationLocation, entry.Path))); } // Call the function passed in and return that result var closure = Args.AsClosure(args, 1); var result = context.InvokeClosure(closure, closure.Frame); if (result.IsErrorValue) { return(EvaluationResult.Error); } return(result); }