/// <summary> /// Evaluates a right-hand side expression if not evaluated yet. /// </summary> protected internal static EvaluationResult EvalExpression(Context context, ModuleLiteral env, EvaluationResult o, EvaluationStackFrame args) { var e = o.Value as Expression; return(e?.Eval(context, env, args) ?? o); }
/// <inheritdoc /> public override EvaluationResult GetOrEvalField(Context context, StringId name, bool recurs, ModuleLiteral origin, LineInfo location) { if (m_values.TryGetValue(name.Value, out EvaluationResult result)) { return(result); } return(EvaluationResult.Undefined); }
/// <summary> /// Creates a task that runs GetOrEvalField taking into consideration concurrency settings /// </summary> protected Task <object> RunGetOrEvalFieldAsync(ImmutableContextBase context, ModuleLiteral origin, SymbolAtom name, ModuleBinding bindingValue) { return(context.EvaluationScheduler.EvaluateValue(() => Task.FromResult(GetOrEvalField(context, origin, name, bindingValue)))); }
/// <summary> /// Gets or evaluates field. /// </summary> /// <returns> /// Returns evaluated field or <see cref="UndefinedValue.Instance"/> if member is not found. /// Unfortunately there is no way to distinguish between type error or missing member, so in both /// cases this method will return the same - undefined. /// </returns> /// <remarks> /// Fields may need to be evaluated. This happens in the case of module fields that have thunked expressions. /// </remarks> public abstract EvaluationResult GetOrEvalField([NotNull] Context context, StringId name, bool recurs, [NotNull] ModuleLiteral origin, LineInfo location);
/// <nodoc /> protected static FullSymbol GetFullyQualifiedBindingName(SymbolTable symbolTable, ModuleLiteral owningModule, SymbolAtom name) { Contract.Requires(name.IsValid); return(owningModule.Id.Name.Combine(symbolTable, name)); }
private void AddGetOrEvalFieldTasks(ImmutableContextBase context, List <Task <object> > list, ModuleLiteral origin) { Contract.Requires(origin.m_bindings != null); foreach (var binding in origin.m_bindings) { SymbolAtom name = binding.Key; ModuleBinding bindingValue = binding.Value; list.Add(RunGetOrEvalFieldAsync(context, origin, name, bindingValue)); } }
/// <summary> /// Gets module or namespace based on module id. /// </summary> public EvaluationResult GetNamespace(ImmutableContextBase context, FullSymbol fullName, bool recurs, ModuleLiteral origin, LineInfo location) { ModuleBinding binding = GetNamespaceBinding(context, fullName, recurs); if (binding == null) { context.Errors.ReportMissingNamespace(origin ?? this, fullName, this, location); return(EvaluationResult.Error); } return(EvaluationResult.Create(binding.Body)); }
private EvaluationResult GetOrEvalField(Context context, SymbolAtom name, ModuleLiteral startEnv, bool recurs, ModuleLiteral origin, LineInfo location) { // This logic is still used only V1 evaluation if (IsFileModule && name == context.ContextTree.CommonConstants.Qualifier) { // Someone references 'qualifier' on the file level. return(EvaluationResult.Create(Qualifier.Qualifier)); } ModuleBinding binding = null; // TODO:ST: sounds reasonable to add "warning" if name was resolved but it is not exposed! if (m_bindings?.TryGetValue(name, out binding) == true && (recurs || binding.IsExported)) { return(GetOrEvalFieldBinding(context, name, binding, location)); } if (recurs && OuterScope != null) { return(OuterScope.GetOrEvalField(context, name, startEnv, true, origin, location)); } context.Errors.ReportMissingMember(origin ?? startEnv, name, startEnv, location); return(EvaluationResult.Error); }
/// <nodoc /> public sealed override EvaluationResult GetOrEvalField(Context context, SymbolAtom name, bool recurs, ModuleLiteral origin, LineInfo location) { Contract.Requires(name.IsValid); return(GetOrEvalField(context, name, this, recurs: recurs, origin: origin, location: location)); }
/// <inheritdoc /> public sealed override bool TryProject(Context context, SymbolAtom name, ModuleLiteral origin, out EvaluationResult result, LineInfo location) { result = GetOrEvalField(context, name, recurs: false, origin: origin, location: Location); return(true); }
/// <inheritdoc /> protected override EvaluationResult DoEval(Context context, ModuleLiteral env, EvaluationStackFrame frame) { return(EvaluationResult.Create(this)); }
/// <summary> /// Constructs module literal for namespace. /// </summary> protected static TypeOrNamespaceModuleLiteral CreateTypeOrNamespaceModule(FullSymbol namespaceName, ModuleLiteral outerScope, LineInfo location) { Contract.Requires(namespaceName.IsValid); Contract.Requires(outerScope != null); Contract.Requires(outerScope.IsFileOrGlobal); ModuleLiteralId moduleId = outerScope.Id.WithName(namespaceName); return(new TypeOrNamespaceModuleLiteral(moduleId, qualifier: QualifierValue.Unqualified, outerScope: outerScope, location: location)); }
private EvaluationResult QualifiedEvaluateWithCycleDetection(ref object value, ImmutableContextBase context, ModuleLiteral env, Evaluation currentEvaluation, ref MutableContextFactory factory) { // Someone else is already busy evaluating this thunk. Let's wait... EvaluationStatus result; var cycleDetector = context.FrontEndHost.CycleDetector; if (cycleDetector != null) { using (cycleDetector.AddValuePromiseChain( valuePromiseChainGetter: () => GetValuePromiseChain(context, env), cycleAnnouncer: () => currentEvaluation.Cancel(EvaluationStatus.Cycle))) { currentEvaluation.Wait(cycleDetector); result = currentEvaluation.Result; if ((result & EvaluationStatus.Value) != 0) { var currentValue = Volatile.Read(ref value); Contract.Assert(currentValue is EvaluationResult); return((EvaluationResult)currentValue); } } } else { currentEvaluation.Wait(); result = currentEvaluation.Result; if ((result & EvaluationStatus.Value) != 0) { var currentValue = Volatile.Read(ref value); Contract.Assert(currentValue is EvaluationResult); return((EvaluationResult)currentValue); } } // Evaluation got canceled --- we hit a cycle (or deadlock)! if ((result & EvaluationStatus.Cycle) != 0) { context.Errors.ReportCycle(env, Expression.Location, factory.ContextName); currentEvaluation.SetValue(ref value, EvaluationResult.Error); return(EvaluationResult.Error); } // Evaluation crashed, which means that Expression.Eval had failed, and had thrown an exception. if ((result & EvaluationStatus.Crash) != 0) { // Crash has been reported at the crash site. currentEvaluation.SetValue(ref value, EvaluationResult.Error); return(EvaluationResult.Error); } return(EvaluationResult.Error); }
private EvaluationResult QualifiedEvaluate( ref object value, ImmutableContextBase context, ModuleLiteral env, EvaluationStackFrame args, ref MutableContextFactory factory) { // Contract.Ensures(Contract.Result<object>() != null && !(Contract.Result<object>() is Evaluation)); // do we have a real value yet? var currentValue = Volatile.Read(ref value); var currentEvaluation = currentValue as Evaluation; if (currentValue != currentEvaluation) { Contract.Assert(currentValue is EvaluationResult); // so it's not null, and it's not an evaluation => we have a real value! return((EvaluationResult)currentValue); } // no real value yet, let's try to create a new evaluation if (currentEvaluation == null && !factory.ForceWaitForResult) { var newEvaluation = new Evaluation( context.EvaluatorConfiguration.CycleDetectorStartupDelay, context.EvaluatorConfiguration.CycleDetectorIncreasePriorityDelay); currentValue = Interlocked.CompareExchange(ref value, newEvaluation, null); currentEvaluation = currentValue as Evaluation; if (currentValue != currentEvaluation) { Contract.Assert(currentValue is EvaluationResult); // so it's not null, and it's not an evaluation => we have a real value! (and the CompareExchange must have failed) return((EvaluationResult)currentValue); } if (currentValue == null) { using (var newLocalMutableContext = factory.Create(context)) { try { var newValue = Expression.Eval(newLocalMutableContext, env, args); // Evaluation got canceled --- we hit a cycle (or deadlock)! if ((newEvaluation.Result & EvaluationStatus.Cycle) != 0) { newLocalMutableContext.Errors.ReportCycle(env, Expression.Location); newEvaluation.SetValue(ref value, EvaluationResult.Error); return(EvaluationResult.Error); } newEvaluation.SetValue(ref value, newValue); return(newValue); } catch { // No actual value was ever set --- this means that Expression.Eval failed, and most likely threw an exception! // Let's propagate this result, so that anyone else waiting for us gets unblocked. newEvaluation.Cancel(EvaluationStatus.Crash); throw; } finally { Contract.Assert(!newLocalMutableContext.HasChildren); // just before the newly created context get disposed, we want to assert that all of its child contexts have already been disposed } } } // there's already an ongoing evaluation! (and the CompareExchange must have failed) we fall through... } if (factory.ForceWaitForResult) { while (currentEvaluation == null) { Thread.Sleep(TimeSpan.FromMilliseconds(10)); currentEvaluation = Volatile.Read(ref value) as Evaluation; } } return(QualifiedEvaluateWithCycleDetection(ref value, context, env, currentEvaluation, ref factory)); }
/// <summary> /// Evaluates this thunk in a new named context /// </summary> /// <remarks> /// V1-specific. Still used to evaluate configuration-related files in V2 (and for the whole V1 legacy evaluation). /// </remarks> public EvaluationResult LegacyEvaluateWithNewNamedContext(ImmutableContextBase context, ModuleLiteral module, FullSymbol contextName, LineInfo location, bool forceWaitForResult = false) { // There is no captured template value in V1 var factory = new MutableContextFactory(this, contextName, module, templateValue: null, location: location, forceWaitForResult: forceWaitForResult); return(EvaluateWithNewNamedContextAndTemplate(context, ref factory)); }