/* * Copyright (c) Contributors, http://opensimulator.org/ * See CONTRIBUTORS.TXT for a full list of copyright holders. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the OpenSimulator Project nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ using System; using System.Collections; using System.Collections.Generic; /** * @brief Collection of variable/function/method definitions */ namespace OpenSim.Region.ScriptEngine.XMREngine { public class VarDict : IEnumerable { public VarDict outerVarDict; // next outer VarDict to search public TokenDeclSDTypeClass thisClass; // this VarDict is for members of thisClass private struct ArgTypes { public TokenType[] argTypes; public bool CanBeCalledBy (TokenType[] calledBy) { if ((argTypes == null) && (calledBy == null)) return true; if ((argTypes == null) || (calledBy == null)) return false; if (argTypes.Length != calledBy.Length) return false; for (int i = argTypes.Length; -- i >= 0;) { if (!TypeCast.IsAssignableFrom (argTypes[i], calledBy[i])) return false; } return true; } public override bool Equals (Object that) { if (that == null) return false; if (that.GetType () != typeof (ArgTypes)) return false; TokenType[] at = this.argTypes; TokenType[] bt = ((ArgTypes)that).argTypes; if ((at == null) && (bt == null)) return true; if ((at == null) || (bt == null)) return false; if (at.Length != bt.Length) return false; for (int i = at.Length; -- i >= 0;) { if (at[i].ToString () != bt[i].ToString ()) return false; } return true; } public override int GetHashCode () { TokenType[] at = this.argTypes; if (at == null) return -1; int hc = 0; for (int i = at.Length; -- i >= 0;) { int c = (hc < 0) ? 1 : 0; hc = hc * 2 + c; hc ^= at[i].ToString ().GetHashCode (); } return hc; } } private struct TDVEntry { public int count; public TokenDeclVar var; } private bool isFrozen = false; private bool locals; private Dictionary> master = new Dictionary> (); private int count = 0; private VarDict frozenLocals = null; /** * @brief Constructor. * @param locals = false: cannot be frozen, allows forward references * true: can be frozen, thus forbidding forward references */ public VarDict (bool locals) { this.locals = locals; } /** * @brief Add new variable to the dictionary. */ public bool AddEntry (TokenDeclVar var) { if (isFrozen) { throw new Exception ("var dict is frozen"); } /* * Make sure we have a sub-dictionary based on the bare name (ie, no signature) */ Dictionary typedic; if (!master.TryGetValue (var.name.val, out typedic)) { typedic = new Dictionary (); master.Add (var.name.val, typedic); } /* * See if there is an entry in the sub-dictionary that matches the argument signature. * Note that fields have null argument lists. * Methods always have a non-null argument list, even if only 0 entries long. */ ArgTypes types; types.argTypes = (var.argDecl == null) ? null : KeyTypesToStringTypes (var.argDecl.types); if (typedic.ContainsKey (types)) return false; /* * It is unique, add to its name-specific sub-dictionary. */ TDVEntry entry; entry.count = ++ count; entry.var = var; typedic.Add (types, entry); return true; } public int Count { get { return count; } } /** * @brief If this is not a local variable frame, just return the frame as is. * If this is a local variable frame, return a version that is frozen, * ie, one that does not contain any future additions. */ public VarDict FreezeLocals () { /* * If not local var frame, return original frame as is. * This will allow forward references as the future additions * will be seen by lookups done in this dictionary. */ if (!locals) return this; /* * If local var frame, return a copy frozen at this point. * This disallows forward referenes as those future additions * will not be seen by lookups done in the frozen dictionary. */ if ((frozenLocals == null) || (frozenLocals.count != this.count)) { /* * Make a copy of the current var dictionary frame. * We copy a reference to the dictionary, and though it may * contain additions made after this point, those additions * will have a count .gt. frozen count and will be ignored. */ frozenLocals = new VarDict (true); frozenLocals.outerVarDict = this.outerVarDict; frozenLocals.thisClass = this.thisClass; frozenLocals.master = this.master; frozenLocals.count = this.count; frozenLocals.frozenLocals = frozenLocals; /* * Mark it as being frozen. * - assert fail if any attempt is made to add to it * - ignore any additions to the dictionary with greater count */ frozenLocals.isFrozen = true; } return frozenLocals; } /** * @brief Find all functions/variables that are callable * @param name = name of function/variable to look for * @param argTypes = the argument types the function is being called with * null to look for a variable * @returns null: no matching function/variable found * else: list of matching functions/variables * for variables, always of length 1 */ private List found = new List (); public TokenDeclVar[] FindCallables (string name, TokenType[] argTypes) { argTypes = KeyTypesToStringTypes (argTypes); TokenDeclVar var = FindExact (name, argTypes); if (var != null) return new TokenDeclVar[] { var }; Dictionary typedic; if (!master.TryGetValue (name, out typedic)) return null; found.Clear (); foreach (KeyValuePair kvp in typedic) { if ((kvp.Value.count <= this.count) && kvp.Key.CanBeCalledBy (argTypes)) { found.Add (kvp.Value.var); } } return (found.Count > 0) ? found.ToArray () : null; } /** * @brief Find exact matching function/variable * @param name = name of function to look for * @param argTypes = argument types the function was declared with * null to look for a variable * @returns null: no matching function/variable found * else: the matching function/variable */ public TokenDeclVar FindExact (string name, TokenType[] argTypes) { /* * Look for list of stuff that matches the given name. */ Dictionary typedic; if (!master.TryGetValue (name, out typedic)) return null; /* * Loop through all fields/methods declared by that name, regardless of arg signature. */ foreach (TDVEntry entry in typedic.Values) { if (entry.count > this.count) continue; TokenDeclVar var = entry.var; /* * Get argument types of declaration. * fields are always null * methods are always non-null, though may be zero-length */ TokenType[] declArgs = (var.argDecl == null) ? null : var.argDecl.types; /* * Convert any key args to string args. */ declArgs = KeyTypesToStringTypes (declArgs); /* * If both are null, they are signature-less (ie, both are fields), and so match. */ if ((declArgs == null) && (argTypes == null)) return var; /* * If calling a delegate, it is a match, regardless of delegate arg types. * If it turns out the arg types do not match, the compiler will give an error * trying to cast the arguments to the delegate arg types. * We don't allow overloading same field name with different delegate types. */ if ((declArgs == null) && (argTypes != null)) { TokenType fieldType = var.type; if (fieldType is TokenTypeSDTypeDelegate) return var; } /* * If not both null, no match, keep looking. */ if ((declArgs == null) || (argTypes == null)) continue; /* * Both not null, match argument types to make sure we have correct overload. */ int i = declArgs.Length; if (i != argTypes.Length) continue; while (-- i >= 0) { string da = declArgs[i].ToString (); string ga = argTypes[i].ToString (); if (da == "key") da = "string"; if (ga == "key") ga = "string"; if (da != ga) break; } if (i < 0) return var; } /* * No match. */ return null; } /** * @brief Replace any TokenTypeKey elements with TokenTypeStr so that * it doesn't matter if functions are declared with key or string, * they will accept either. * @param argTypes = argument types as declared in source code * @returns argTypes with any key replaced by string */ private static TokenType[] KeyTypesToStringTypes (TokenType[] argTypes) { if (argTypes != null) { int i; int nats = argTypes.Length; for (i = nats; -- i >= 0;) { if (argTypes[i] is TokenTypeKey) break; } if (i >= 0) { TokenType[] at = new TokenType[nats]; for (i = nats; -- i >= 0;) { at[i] = argTypes[i]; if (argTypes[i] is TokenTypeKey) { at[i] = new TokenTypeStr (argTypes[i]); } } return at; } } return argTypes; } // foreach goes through all the TokenDeclVars that were added // IEnumerable public IEnumerator GetEnumerator () { return new VarDictEnumerator (this.master, this.count); } private class VarDictEnumerator : IEnumerator { private IEnumerator masterEnum; private IEnumerator typedicEnum; private int count; public VarDictEnumerator (Dictionary> master, int count) { masterEnum = master.Values.GetEnumerator (); this.count = count; } // IEnumerator public void Reset () { masterEnum.Reset (); typedicEnum = null; } // IEnumerator public bool MoveNext () { while (true) { if (typedicEnum != null) { while (typedicEnum.MoveNext ()) { if (((TDVEntry)typedicEnum.Current).count <= this.count) return true; } typedicEnum = null; } if (!masterEnum.MoveNext ()) return false; Dictionary ctd; ctd = (Dictionary)masterEnum.Current; typedicEnum = ctd.Values.GetEnumerator (); } } // IEnumerator public object Current { get { return ((TDVEntry)typedicEnum.Current).var; } } } } }