/* * Copyright © 2011, Petro Protsyk, Denys Vuika * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System; namespace Scripting.SSharp.Runtime { /// /// Base implementation of ScriptContext object /// public class ScriptContext : IScriptContext { #region Fields private ContextFlags _flags = ContextFlags.Empty; #endregion #region Properties /// /// Scope object /// public IScriptScope Scope { get; private set; } public Script Owner { get; private set; } /// /// Script Result object /// public object Result { get; set; } #endregion #region Constructors /// /// Creates new Script Context with Default scope /// public ScriptContext() { CreateScope(); } #endregion #region Scope /// /// Creates new default nested scope /// public IScriptScope CreateScope() { Scope = RuntimeHost.ScopeFactory.Create(ScopeTypes.Default, Scope); return Scope; } /// /// Replace existing scope with new one /// /// public IScriptScope CreateScope(IScriptScope scope) { if (scope.Parent != Scope) throw new ScriptRuntimeException(Strings.ScopeParentIsNotValid); Scope = scope; return Scope; } /// /// Remove Local Scope /// public void RemoveLocalScope() { if (Scope.Parent != null) { IScriptScope scopeToRemove = Scope; Scope = Scope.Parent; scopeToRemove.Dispose(); } else throw new Exception("Can't remove global scope, use Scope.Clean"); } public object GetItem(string id, bool throwException) { return Scope.GetItem(id, throwException); } public void SetItem(string id, object value) { Scope.SetItem(id, value); } public IValueReference Ref(string id) { var args = new ReferencingEventArgs(false, null); if (OnReferencing(args)) return args.Ref; var scope = Scope; while (scope != null) { //TODO: Figure out more maintainable solution if (scope is FunctionScope) return null; if (scope.HasVariable(id)) { args = new ReferencingEventArgs(false, scope.Ref(id)); if (args.Ref != null && OnReferenced(args)) { if (Owner != null) { Owner.NotifyReferenceCreated(args.Ref); } return args.Ref; } return null; } scope = scope.Parent; } return null; } public event EventHandler Referencing; protected virtual bool OnReferencing(ReferencingEventArgs args) { if (Referencing != null) Referencing.Invoke(this, args); return args.Cancel; } public event EventHandler Referenced; protected virtual bool OnReferenced(ReferencingEventArgs args) { if (Referenced != null) Referenced.Invoke(this, args); return !args.Cancel; } #endregion #region Break-Continue-Return /// /// Set return state of run-time /// /// true or false public void SetReturn(bool val) { if (val && IsContinue()) throw new ScriptRuntimeException(Strings.ContextHasCorruptedFlagsError); if (val && IsBreak()) throw new ScriptRuntimeException(Strings.ContextHasCorruptedFlagsError); if (val) _flags = _flags | ContextFlags.Return; else _flags = _flags & ~ContextFlags.Return; } /// /// Set break state of run-time /// /// true or false public void SetBreak(bool val) { if (val && IsContinue()) throw new ScriptRuntimeException(Strings.ContextHasCorruptedFlagsError); if (val) _flags = _flags | ContextFlags.Break; else _flags = _flags & ~ContextFlags.Break; } /// /// Set continue state of run-time /// /// true or false public void SetContinue(bool val) { if (val && IsBreak()) throw new ScriptRuntimeException(Strings.ContextHasCorruptedFlagsError); if (val) _flags = _flags | ContextFlags.Continue; else _flags = _flags & ~ContextFlags.Continue; } /// /// Reset all flags that control execution. Called on each context /// before and after script execution /// public void ResetControlFlags() { _flags = ContextFlags.Empty; } /// /// Return state /// /// true or false public bool IsReturn() { return (_flags & ContextFlags.Return) == ContextFlags.Return; } /// /// Break state /// /// true or false public bool IsBreak() { return (_flags & ContextFlags.Break) == ContextFlags.Break; } /// /// Continue state /// /// true or false public bool IsContinue() { return (_flags & ContextFlags.Continue) == ContextFlags.Continue; } #endregion #region Function Defs /// /// Finds function definition in current scope /// /// function name /// IInvokable object public IInvokable GetFunctionDefinition(string name) { return Scope.GetFunctionDefinition(name); } #endregion #region Internal Methods internal void SetOwner(Script owner) { //Remove context from previous owner if (Owner != null && owner != null && Owner != owner) Owner.Context = null; Owner = owner; } #endregion #region IDisposable Members private bool _disposed; protected bool Disposed { get { lock (this) { return _disposed; } } } public void Dispose() { lock (this) { if (_disposed == false) { Cleanup(); _disposed = true; GC.SuppressFinalize(this); } } } ~ScriptContext() { Cleanup(); } protected virtual void Cleanup() { IScriptScope s = Scope; while (s != null) { IScriptScope toRemove = s; s = s.Parent; toRemove.Dispose(); } Result = null; Scope = null; } #endregion } /// /// Specify context state /// [Flags] public enum ContextFlags { /// /// Initial state /// Empty = 0, /// /// Brake operator executed /// Break = 2, /// /// Continue operator executed /// Continue = 4, /// /// Return statement executed /// Return = 8 } public class ReferencingEventArgs : EventArgs { public bool Cancel { get; set; } public IValueReference Ref { get; set; } public ReferencingEventArgs() : this(false, null) { } public ReferencingEventArgs(bool cancel, IValueReference reference) { Cancel = cancel; Ref = reference; } } }